Przeglądaj źródła

refactor TypeInfo into an interface

Make TypeInfo an interface in preperation for implementing
marshalling logic for Tuples and potentially UDT's and other
types which Cassandra might add in the future.

The previous implementation was rigid in that it assumed a single
returned column for a returned type, but with tuples and UDT's this
is no longer the case as the tuple will have a single TypeInfo for
multiple columns.

This change will break existing clients which implement MarshalCQL
or UnmarshalCQL as the TypeInfo is passed in is not a pointer so the
interface type assertion will not match the expected interface.
Chris Bannister 10 lat temu
rodzic
commit
4b4dcdde53
8 zmienionych plików z 418 dodań i 312 usunięć
  1. 12 12
      cassandra_test.go
  2. 29 26
      frame.go
  3. 4 10
      helpers.go
  4. 148 78
      marshal.go
  5. 126 100
      marshal_test.go
  6. 23 17
      metadata.go
  7. 73 66
      metadata_test.go
  8. 3 3
      session.go

+ 12 - 12
cassandra_test.go

@@ -502,11 +502,11 @@ type FullName struct {
 	LastName  string
 	LastName  string
 }
 }
 
 
-func (n FullName) MarshalCQL(info *TypeInfo) ([]byte, error) {
+func (n FullName) MarshalCQL(info TypeInfo) ([]byte, error) {
 	return []byte(n.FirstName + " " + n.LastName), nil
 	return []byte(n.FirstName + " " + n.LastName), nil
 }
 }
 
 
-func (n *FullName) UnmarshalCQL(info *TypeInfo, data []byte) error {
+func (n *FullName) UnmarshalCQL(info TypeInfo, data []byte) error {
 	t := strings.SplitN(string(data), " ", 2)
 	t := strings.SplitN(string(data), " ", 2)
 	n.FirstName, n.LastName = t[0], t[1]
 	n.FirstName, n.LastName = t[0], t[1]
 	return nil
 	return nil
@@ -981,8 +981,8 @@ func injectInvalidPreparedStatement(t *testing.T, session *Session, table string
 					Keyspace: "gocql_test",
 					Keyspace: "gocql_test",
 					Table:    table,
 					Table:    table,
 					Name:     "foo",
 					Name:     "foo",
-					TypeInfo: &TypeInfo{
-						Type: TypeVarchar,
+					TypeInfo: NativeType{
+						typ: TypeVarchar,
 					},
 					},
 				},
 				},
 			}},
 			}},
@@ -1741,8 +1741,8 @@ func TestRoutingKey(t *testing.T) {
 	if routingKeyInfo.types[0] == nil {
 	if routingKeyInfo.types[0] == nil {
 		t.Fatal("Expected routing key types[0] to be non-nil")
 		t.Fatal("Expected routing key types[0] to be non-nil")
 	}
 	}
-	if routingKeyInfo.types[0].Type != TypeInt {
-		t.Fatalf("Expected routing key types[0].Type to be %v but was %v", TypeInt, routingKeyInfo.types[0])
+	if routingKeyInfo.types[0].Type() != TypeInt {
+		t.Fatalf("Expected routing key types[0].Type to be %v but was %v", TypeInt, routingKeyInfo.types[0].Type())
 	}
 	}
 
 
 	// verify the cache is working
 	// verify the cache is working
@@ -1762,8 +1762,8 @@ func TestRoutingKey(t *testing.T) {
 	if routingKeyInfo.types[0] == nil {
 	if routingKeyInfo.types[0] == nil {
 		t.Fatal("Expected routing key types[0] to be non-nil")
 		t.Fatal("Expected routing key types[0] to be non-nil")
 	}
 	}
-	if routingKeyInfo.types[0].Type != TypeInt {
-		t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[0])
+	if routingKeyInfo.types[0].Type() != TypeInt {
+		t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[0].Type())
 	}
 	}
 	cacheSize := session.routingKeyInfoCache.lru.Len()
 	cacheSize := session.routingKeyInfoCache.lru.Len()
 	if cacheSize != 1 {
 	if cacheSize != 1 {
@@ -1802,14 +1802,14 @@ func TestRoutingKey(t *testing.T) {
 	if routingKeyInfo.types[0] == nil {
 	if routingKeyInfo.types[0] == nil {
 		t.Fatal("Expected routing key types[0] to be non-nil")
 		t.Fatal("Expected routing key types[0] to be non-nil")
 	}
 	}
-	if routingKeyInfo.types[0].Type != TypeInt {
-		t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[0])
+	if routingKeyInfo.types[0].Type() != TypeInt {
+		t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[0].Type())
 	}
 	}
 	if routingKeyInfo.types[1] == nil {
 	if routingKeyInfo.types[1] == nil {
 		t.Fatal("Expected routing key types[1] to be non-nil")
 		t.Fatal("Expected routing key types[1] to be non-nil")
 	}
 	}
-	if routingKeyInfo.types[1].Type != TypeInt {
-		t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[1])
+	if routingKeyInfo.types[1].Type() != TypeInt {
+		t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[1].Type())
 	}
 	}
 
 
 	query = session.Query("SELECT * FROM test_composite_routing_key WHERE second_id=? AND first_id=?", 1, 2)
 	query = session.Query("SELECT * FROM test_composite_routing_key WHERE second_id=? AND first_id=?", 1, 2)

+ 29 - 26
frame.go

@@ -569,38 +569,41 @@ func (f *framer) writePrepareFrame(stream int, statement string) error {
 	return f.finishWrite()
 	return f.finishWrite()
 }
 }
 
 
-func (f *framer) readTypeInfo() *TypeInfo {
+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()
 	id := f.readShort()
-	typ := &TypeInfo{
-		// we need to pass proto to the marshaller here
-		Proto: f.proto,
-		Type:  Type(id),
+
+	simple := NativeType{
+		proto: f.proto,
+		typ:   Type(id),
 	}
 	}
 
 
-	switch typ.Type {
-	case TypeCustom:
-		typ.Custom = f.readString()
-		if cassType := getApacheCassandraType(typ.Custom); cassType != TypeCustom {
-			typ = &TypeInfo{
-				Proto: f.proto,
-				Type:  cassType,
-			}
-			switch typ.Type {
-			case TypeMap:
-				typ.Key = f.readTypeInfo()
-				fallthrough
-			case TypeList, TypeSet:
-				typ.Elem = f.readTypeInfo()
-			}
+	if simple.typ == TypeCustom {
+		simple.custom = f.readString()
+		if cassType := getApacheCassandraType(simple.custom); cassType != TypeCustom {
+			simple.typ = cassType
+		}
+	}
+
+	switch simple.typ {
+	case TypeTuple:
+		panic("not implemented")
+	case TypeMap, TypeList, TypeSet:
+		collection := CollectionType{
+			NativeType: simple,
+		}
+
+		if simple.typ == TypeMap {
+			collection.Key = f.readTypeInfo()
 		}
 		}
-	case TypeMap:
-		typ.Key = f.readTypeInfo()
-		fallthrough
-	case TypeList, TypeSet:
-		typ.Elem = f.readTypeInfo()
+
+		collection.Elem = f.readTypeInfo()
+
+		return collection
 	}
 	}
 
 
-	return typ
+	return simple
 }
 }
 
 
 type resultMetadata struct {
 type resultMetadata struct {

+ 4 - 10
helpers.go

@@ -18,14 +18,8 @@ type RowData struct {
 	Values  []interface{}
 	Values  []interface{}
 }
 }
 
 
-// New creates a pointer to an empty version of whatever type
-// is referenced by the TypeInfo receiver
-func (t *TypeInfo) New() interface{} {
-	return reflect.New(goType(t)).Interface()
-}
-
-func goType(t *TypeInfo) reflect.Type {
-	switch t.Type {
+func goType(t TypeInfo) reflect.Type {
+	switch t.Type() {
 	case TypeVarchar, TypeAscii, TypeInet:
 	case TypeVarchar, TypeAscii, TypeInet:
 		return reflect.TypeOf(*new(string))
 		return reflect.TypeOf(*new(string))
 	case TypeBigInt, TypeCounter:
 	case TypeBigInt, TypeCounter:
@@ -47,9 +41,9 @@ func goType(t *TypeInfo) reflect.Type {
 	case TypeUUID, TypeTimeUUID:
 	case TypeUUID, TypeTimeUUID:
 		return reflect.TypeOf(*new(UUID))
 		return reflect.TypeOf(*new(UUID))
 	case TypeList, TypeSet:
 	case TypeList, TypeSet:
-		return reflect.SliceOf(goType(t.Elem))
+		return reflect.SliceOf(goType(t.(CollectionType).Elem))
 	case TypeMap:
 	case TypeMap:
-		return reflect.MapOf(goType(t.Key), goType(t.Elem))
+		return reflect.MapOf(goType(t.(CollectionType).Key), goType(t.(CollectionType).Elem))
 	case TypeVarint:
 	case TypeVarint:
 		return reflect.TypeOf(*new(*big.Int))
 		return reflect.TypeOf(*new(*big.Int))
 	default:
 	default:

+ 148 - 78
marshal.go

@@ -25,22 +25,22 @@ var (
 // Marshaler is the interface implemented by objects that can marshal
 // Marshaler is the interface implemented by objects that can marshal
 // themselves into values understood by Cassandra.
 // themselves into values understood by Cassandra.
 type Marshaler interface {
 type Marshaler interface {
-	MarshalCQL(info *TypeInfo) ([]byte, error)
+	MarshalCQL(info TypeInfo) ([]byte, error)
 }
 }
 
 
 // Unmarshaler is the interface implemented by objects that can unmarshal
 // Unmarshaler is the interface implemented by objects that can unmarshal
 // a Cassandra specific description of themselves.
 // a Cassandra specific description of themselves.
 type Unmarshaler interface {
 type Unmarshaler interface {
-	UnmarshalCQL(info *TypeInfo, data []byte) error
+	UnmarshalCQL(info TypeInfo, data []byte) error
 }
 }
 
 
 // Marshal returns the CQL encoding of the value for the Cassandra
 // Marshal returns the CQL encoding of the value for the Cassandra
 // internal type described by the info parameter.
 // internal type described by the info parameter.
-func Marshal(info *TypeInfo, value interface{}) ([]byte, error) {
+func Marshal(info TypeInfo, value interface{}) ([]byte, error) {
 	if value == nil {
 	if value == nil {
 		return nil, nil
 		return nil, nil
 	}
 	}
-	if info.Proto < protoVersion1 {
+	if info.Version() < protoVersion1 {
 		panic("protocol version not set")
 		panic("protocol version not set")
 	}
 	}
 
 
@@ -56,7 +56,7 @@ func Marshal(info *TypeInfo, value interface{}) ([]byte, error) {
 		}
 		}
 	}
 	}
 
 
-	switch info.Type {
+	switch info.Type() {
 	case TypeVarchar, TypeAscii, TypeBlob:
 	case TypeVarchar, TypeAscii, TypeBlob:
 		return marshalVarchar(info, value)
 		return marshalVarchar(info, value)
 	case TypeBoolean:
 	case TypeBoolean:
@@ -91,7 +91,7 @@ func Marshal(info *TypeInfo, value interface{}) ([]byte, error) {
 // Unmarshal parses the CQL encoded data based on the info parameter that
 // Unmarshal parses the CQL encoded data based on the info parameter that
 // describes the Cassandra internal data type and stores the result in the
 // describes the Cassandra internal data type and stores the result in the
 // value pointed by value.
 // value pointed by value.
-func Unmarshal(info *TypeInfo, data []byte, value interface{}) error {
+func Unmarshal(info TypeInfo, data []byte, value interface{}) error {
 	if v, ok := value.(Unmarshaler); ok {
 	if v, ok := value.(Unmarshaler); ok {
 		return v.UnmarshalCQL(info, data)
 		return v.UnmarshalCQL(info, data)
 	}
 	}
@@ -99,7 +99,7 @@ func Unmarshal(info *TypeInfo, data []byte, value interface{}) error {
 		return unmarshalNullable(info, data, value)
 		return unmarshalNullable(info, data, value)
 	}
 	}
 
 
-	switch info.Type {
+	switch info.Type() {
 	case TypeVarchar, TypeAscii, TypeBlob:
 	case TypeVarchar, TypeAscii, TypeBlob:
 		return unmarshalVarchar(info, data, value)
 		return unmarshalVarchar(info, data, value)
 	case TypeBoolean:
 	case TypeBoolean:
@@ -138,25 +138,25 @@ func isNullableValue(value interface{}) bool {
 	return v.Kind() == reflect.Ptr && v.Type().Elem().Kind() == reflect.Ptr
 	return v.Kind() == reflect.Ptr && v.Type().Elem().Kind() == reflect.Ptr
 }
 }
 
 
-func isNullData(info *TypeInfo, data []byte) bool {
+func isNullData(info TypeInfo, data []byte) bool {
 	return len(data) == 0
 	return len(data) == 0
 }
 }
 
 
-func unmarshalNullable(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalNullable(info TypeInfo, data []byte, value interface{}) error {
 	valueRef := reflect.ValueOf(value)
 	valueRef := reflect.ValueOf(value)
 
 
 	if isNullData(info, data) {
 	if isNullData(info, data) {
 		nilValue := reflect.Zero(valueRef.Type().Elem())
 		nilValue := reflect.Zero(valueRef.Type().Elem())
 		valueRef.Elem().Set(nilValue)
 		valueRef.Elem().Set(nilValue)
 		return nil
 		return nil
-	} else {
-		newValue := reflect.New(valueRef.Type().Elem().Elem())
-		valueRef.Elem().Set(newValue)
-		return Unmarshal(info, data, newValue.Interface())
 	}
 	}
+
+	newValue := reflect.New(valueRef.Type().Elem().Elem())
+	valueRef.Elem().Set(newValue)
+	return Unmarshal(info, data, newValue.Interface())
 }
 }
 
 
-func marshalVarchar(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalVarchar(info TypeInfo, value interface{}) ([]byte, error) {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Marshaler:
 	case Marshaler:
 		return v.MarshalCQL(info)
 		return v.MarshalCQL(info)
@@ -177,7 +177,7 @@ func marshalVarchar(info *TypeInfo, value interface{}) ([]byte, error) {
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 }
 }
 
 
-func unmarshalVarchar(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalVarchar(info TypeInfo, data []byte, value interface{}) error {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Unmarshaler:
 	case Unmarshaler:
 		return v.UnmarshalCQL(info, data)
 		return v.UnmarshalCQL(info, data)
@@ -216,7 +216,7 @@ func unmarshalVarchar(info *TypeInfo, data []byte, value interface{}) error {
 	return unmarshalErrorf("can not unmarshal %s into %T", info, value)
 	return unmarshalErrorf("can not unmarshal %s into %T", info, value)
 }
 }
 
 
-func marshalInt(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalInt(info TypeInfo, value interface{}) ([]byte, error) {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Marshaler:
 	case Marshaler:
 		return v.MarshalCQL(info)
 		return v.MarshalCQL(info)
@@ -291,7 +291,7 @@ func decInt(x []byte) int32 {
 	return int32(x[0])<<24 | int32(x[1])<<16 | int32(x[2])<<8 | int32(x[3])
 	return int32(x[0])<<24 | int32(x[1])<<16 | int32(x[2])<<8 | int32(x[3])
 }
 }
 
 
-func marshalBigInt(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalBigInt(info TypeInfo, value interface{}) ([]byte, error) {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Marshaler:
 	case Marshaler:
 		return v.MarshalCQL(info)
 		return v.MarshalCQL(info)
@@ -357,15 +357,15 @@ func bytesToInt64(data []byte) (ret int64) {
 	return ret
 	return ret
 }
 }
 
 
-func unmarshalBigInt(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalBigInt(info TypeInfo, data []byte, value interface{}) error {
 	return unmarshalIntlike(info, decBigInt(data), data, value)
 	return unmarshalIntlike(info, decBigInt(data), data, value)
 }
 }
 
 
-func unmarshalInt(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalInt(info TypeInfo, data []byte, value interface{}) error {
 	return unmarshalIntlike(info, int64(decInt(data)), data, value)
 	return unmarshalIntlike(info, int64(decInt(data)), data, value)
 }
 }
 
 
-func unmarshalVarint(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalVarint(info TypeInfo, data []byte, value interface{}) error {
 	switch value.(type) {
 	switch value.(type) {
 	case *big.Int:
 	case *big.Int:
 		return unmarshalIntlike(info, 0, data, value)
 		return unmarshalIntlike(info, 0, data, value)
@@ -382,7 +382,7 @@ func unmarshalVarint(info *TypeInfo, data []byte, value interface{}) error {
 	return unmarshalIntlike(info, int64Val, data, value)
 	return unmarshalIntlike(info, int64Val, data, value)
 }
 }
 
 
-func marshalVarint(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalVarint(info TypeInfo, value interface{}) ([]byte, error) {
 	var (
 	var (
 		retBytes []byte
 		retBytes []byte
 		err      error
 		err      error
@@ -431,7 +431,7 @@ func marshalVarint(info *TypeInfo, value interface{}) ([]byte, error) {
 	return retBytes, err
 	return retBytes, err
 }
 }
 
 
-func unmarshalIntlike(info *TypeInfo, int64Val int64, data []byte, value interface{}) error {
+func unmarshalIntlike(info TypeInfo, int64Val int64, data []byte, value interface{}) error {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case *int:
 	case *int:
 		if ^uint(0) == math.MaxUint32 && (int64Val < math.MinInt32 || int64Val > math.MaxInt32) {
 		if ^uint(0) == math.MaxUint32 && (int64Val < math.MinInt32 || int64Val > math.MaxInt32) {
@@ -576,7 +576,7 @@ func decBigInt(data []byte) int64 {
 		int64(data[6])<<8 | int64(data[7])
 		int64(data[6])<<8 | int64(data[7])
 }
 }
 
 
-func marshalBool(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalBool(info TypeInfo, value interface{}) ([]byte, error) {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Marshaler:
 	case Marshaler:
 		return v.MarshalCQL(info)
 		return v.MarshalCQL(info)
@@ -598,7 +598,7 @@ func encBool(v bool) []byte {
 	return []byte{0}
 	return []byte{0}
 }
 }
 
 
-func unmarshalBool(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalBool(info TypeInfo, data []byte, value interface{}) error {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Unmarshaler:
 	case Unmarshaler:
 		return v.UnmarshalCQL(info, data)
 		return v.UnmarshalCQL(info, data)
@@ -626,7 +626,7 @@ func decBool(v []byte) bool {
 	return v[0] != 0
 	return v[0] != 0
 }
 }
 
 
-func marshalFloat(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalFloat(info TypeInfo, value interface{}) ([]byte, error) {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Marshaler:
 	case Marshaler:
 		return v.MarshalCQL(info)
 		return v.MarshalCQL(info)
@@ -641,7 +641,7 @@ func marshalFloat(info *TypeInfo, value interface{}) ([]byte, error) {
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 }
 }
 
 
-func unmarshalFloat(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalFloat(info TypeInfo, data []byte, value interface{}) error {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Unmarshaler:
 	case Unmarshaler:
 		return v.UnmarshalCQL(info, data)
 		return v.UnmarshalCQL(info, data)
@@ -662,7 +662,7 @@ func unmarshalFloat(info *TypeInfo, data []byte, value interface{}) error {
 	return unmarshalErrorf("can not unmarshal %s into %T", info, value)
 	return unmarshalErrorf("can not unmarshal %s into %T", info, value)
 }
 }
 
 
-func marshalDouble(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalDouble(info TypeInfo, value interface{}) ([]byte, error) {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Marshaler:
 	case Marshaler:
 		return v.MarshalCQL(info)
 		return v.MarshalCQL(info)
@@ -677,7 +677,7 @@ func marshalDouble(info *TypeInfo, value interface{}) ([]byte, error) {
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 }
 }
 
 
-func unmarshalDouble(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalDouble(info TypeInfo, data []byte, value interface{}) error {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Unmarshaler:
 	case Unmarshaler:
 		return v.UnmarshalCQL(info, data)
 		return v.UnmarshalCQL(info, data)
@@ -698,7 +698,7 @@ func unmarshalDouble(info *TypeInfo, data []byte, value interface{}) error {
 	return unmarshalErrorf("can not unmarshal %s into %T", info, value)
 	return unmarshalErrorf("can not unmarshal %s into %T", info, value)
 }
 }
 
 
-func marshalDecimal(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalDecimal(info TypeInfo, value interface{}) ([]byte, error) {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Marshaler:
 	case Marshaler:
 		return v.MarshalCQL(info)
 		return v.MarshalCQL(info)
@@ -716,7 +716,7 @@ func marshalDecimal(info *TypeInfo, value interface{}) ([]byte, error) {
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 }
 }
 
 
-func unmarshalDecimal(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalDecimal(info TypeInfo, data []byte, value interface{}) error {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Unmarshaler:
 	case Unmarshaler:
 		return v.UnmarshalCQL(info, data)
 		return v.UnmarshalCQL(info, data)
@@ -769,7 +769,7 @@ func encBigInt2C(n *big.Int) []byte {
 	return nil
 	return nil
 }
 }
 
 
-func marshalTimestamp(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalTimestamp(info TypeInfo, value interface{}) ([]byte, error) {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Marshaler:
 	case Marshaler:
 		return v.MarshalCQL(info)
 		return v.MarshalCQL(info)
@@ -787,7 +787,7 @@ func marshalTimestamp(info *TypeInfo, value interface{}) ([]byte, error) {
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 }
 }
 
 
-func unmarshalTimestamp(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalTimestamp(info TypeInfo, data []byte, value interface{}) error {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Unmarshaler:
 	case Unmarshaler:
 		return v.UnmarshalCQL(info, data)
 		return v.UnmarshalCQL(info, data)
@@ -817,8 +817,8 @@ func unmarshalTimestamp(info *TypeInfo, data []byte, value interface{}) error {
 	return unmarshalErrorf("can not unmarshal %s into %T", info, value)
 	return unmarshalErrorf("can not unmarshal %s into %T", info, value)
 }
 }
 
 
-func writeCollectionSize(info *TypeInfo, n int, buf *bytes.Buffer) error {
-	if info.Proto > protoVersion2 {
+func writeCollectionSize(info CollectionType, n int, buf *bytes.Buffer) error {
+	if info.proto > protoVersion2 {
 		if n > math.MaxInt32 {
 		if n > math.MaxInt32 {
 			return marshalErrorf("marshal: collection too large")
 			return marshalErrorf("marshal: collection too large")
 		}
 		}
@@ -839,7 +839,12 @@ func writeCollectionSize(info *TypeInfo, n int, buf *bytes.Buffer) error {
 	return nil
 	return nil
 }
 }
 
 
-func marshalList(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalList(info TypeInfo, value interface{}) ([]byte, error) {
+	listInfo, ok := info.(CollectionType)
+	if !ok {
+		return nil, marshalErrorf("marshal: can not marshal non collection type into list")
+	}
+
 	rv := reflect.ValueOf(value)
 	rv := reflect.ValueOf(value)
 	t := rv.Type()
 	t := rv.Type()
 	k := t.Kind()
 	k := t.Kind()
@@ -851,23 +856,22 @@ func marshalList(info *TypeInfo, value interface{}) ([]byte, error) {
 		buf := &bytes.Buffer{}
 		buf := &bytes.Buffer{}
 		n := rv.Len()
 		n := rv.Len()
 
 
-		if err := writeCollectionSize(info, n, buf); err != nil {
+		if err := writeCollectionSize(listInfo, n, buf); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 
 
 		for i := 0; i < n; i++ {
 		for i := 0; i < n; i++ {
-			item, err := Marshal(info.Elem, rv.Index(i).Interface())
+			item, err := Marshal(listInfo.Elem, rv.Index(i).Interface())
 			if err != nil {
 			if err != nil {
 				return nil, err
 				return nil, err
 			}
 			}
-			if err := writeCollectionSize(info, len(item), buf); err != nil {
+			if err := writeCollectionSize(listInfo, len(item), buf); err != nil {
 				return nil, err
 				return nil, err
 			}
 			}
 			buf.Write(item)
 			buf.Write(item)
 		}
 		}
 		return buf.Bytes(), nil
 		return buf.Bytes(), nil
-	}
-	if k == reflect.Map {
+	case reflect.Map:
 		elem := t.Elem()
 		elem := t.Elem()
 		if elem.Kind() == reflect.Struct && elem.NumField() == 0 {
 		if elem.Kind() == reflect.Struct && elem.NumField() == 0 {
 			rkeys := rv.MapKeys()
 			rkeys := rv.MapKeys()
@@ -875,14 +879,14 @@ func marshalList(info *TypeInfo, value interface{}) ([]byte, error) {
 			for i := 0; i < len(keys); i++ {
 			for i := 0; i < len(keys); i++ {
 				keys[i] = rkeys[i].Interface()
 				keys[i] = rkeys[i].Interface()
 			}
 			}
-			return marshalList(info, keys)
+			return marshalList(listInfo, keys)
 		}
 		}
 	}
 	}
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 }
 }
 
 
-func readCollectionSize(info *TypeInfo, data []byte) (size, read int) {
-	if info.Proto > protoVersion2 {
+func readCollectionSize(info CollectionType, data []byte) (size, read int) {
+	if info.proto > protoVersion2 {
 		size = int(data[0])<<24 | int(data[1])<<16 | int(data[2])<<8 | int(data[3])
 		size = int(data[0])<<24 | int(data[1])<<16 | int(data[2])<<8 | int(data[3])
 		read = 4
 		read = 4
 	} else {
 	} else {
@@ -892,7 +896,12 @@ func readCollectionSize(info *TypeInfo, data []byte) (size, read int) {
 	return
 	return
 }
 }
 
 
-func unmarshalList(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalList(info TypeInfo, data []byte, value interface{}) error {
+	listInfo, ok := info.(CollectionType)
+	if !ok {
+		return unmarshalErrorf("unmarshal: can not unmarshal none collection type into list")
+	}
+
 	rv := reflect.ValueOf(value)
 	rv := reflect.ValueOf(value)
 	if rv.Kind() != reflect.Ptr {
 	if rv.Kind() != reflect.Ptr {
 		return unmarshalErrorf("can not unmarshal into non-pointer %T", value)
 		return unmarshalErrorf("can not unmarshal into non-pointer %T", value)
@@ -913,7 +922,7 @@ func unmarshalList(info *TypeInfo, data []byte, value interface{}) error {
 		if len(data) < 2 {
 		if len(data) < 2 {
 			return unmarshalErrorf("unmarshal list: unexpected eof")
 			return unmarshalErrorf("unmarshal list: unexpected eof")
 		}
 		}
-		n, p := readCollectionSize(info, data)
+		n, p := readCollectionSize(listInfo, data)
 		data = data[p:]
 		data = data[p:]
 		if k == reflect.Array {
 		if k == reflect.Array {
 			if rv.Len() != n {
 			if rv.Len() != n {
@@ -928,9 +937,9 @@ func unmarshalList(info *TypeInfo, data []byte, value interface{}) error {
 			if len(data) < 2 {
 			if len(data) < 2 {
 				return unmarshalErrorf("unmarshal list: unexpected eof")
 				return unmarshalErrorf("unmarshal list: unexpected eof")
 			}
 			}
-			m, p := readCollectionSize(info, data)
+			m, p := readCollectionSize(listInfo, data)
 			data = data[p:]
 			data = data[p:]
-			if err := Unmarshal(info.Elem, data[:m], rv.Index(i).Addr().Interface()); err != nil {
+			if err := Unmarshal(listInfo.Elem, data[:m], rv.Index(i).Addr().Interface()); err != nil {
 				return err
 				return err
 			}
 			}
 			data = data[m:]
 			data = data[m:]
@@ -940,7 +949,12 @@ func unmarshalList(info *TypeInfo, data []byte, value interface{}) error {
 	return unmarshalErrorf("can not unmarshal %s into %T", info, value)
 	return unmarshalErrorf("can not unmarshal %s into %T", info, value)
 }
 }
 
 
-func marshalMap(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalMap(info TypeInfo, value interface{}) ([]byte, error) {
+	mapInfo, ok := info.(CollectionType)
+	if !ok {
+		return nil, marshalErrorf("marshal: can not marshal none collection type into map")
+	}
+
 	rv := reflect.ValueOf(value)
 	rv := reflect.ValueOf(value)
 	t := rv.Type()
 	t := rv.Type()
 	if t.Kind() != reflect.Map {
 	if t.Kind() != reflect.Map {
@@ -952,26 +966,26 @@ func marshalMap(info *TypeInfo, value interface{}) ([]byte, error) {
 	buf := &bytes.Buffer{}
 	buf := &bytes.Buffer{}
 	n := rv.Len()
 	n := rv.Len()
 
 
-	if err := writeCollectionSize(info, n, buf); err != nil {
+	if err := writeCollectionSize(mapInfo, n, buf); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	keys := rv.MapKeys()
 	keys := rv.MapKeys()
 	for _, key := range keys {
 	for _, key := range keys {
-		item, err := Marshal(info.Key, key.Interface())
+		item, err := Marshal(mapInfo.Key, key.Interface())
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		if err := writeCollectionSize(info, len(item), buf); err != nil {
+		if err := writeCollectionSize(mapInfo, len(item), buf); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 		buf.Write(item)
 		buf.Write(item)
 
 
-		item, err = Marshal(info.Elem, rv.MapIndex(key).Interface())
+		item, err = Marshal(mapInfo.Elem, rv.MapIndex(key).Interface())
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		if err := writeCollectionSize(info, len(item), buf); err != nil {
+		if err := writeCollectionSize(mapInfo, len(item), buf); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 		buf.Write(item)
 		buf.Write(item)
@@ -979,7 +993,12 @@ func marshalMap(info *TypeInfo, value interface{}) ([]byte, error) {
 	return buf.Bytes(), nil
 	return buf.Bytes(), nil
 }
 }
 
 
-func unmarshalMap(info *TypeInfo, data []byte, value interface{}) error {
+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")
+	}
+
 	rv := reflect.ValueOf(value)
 	rv := reflect.ValueOf(value)
 	if rv.Kind() != reflect.Ptr {
 	if rv.Kind() != reflect.Ptr {
 		return unmarshalErrorf("can not unmarshal into non-pointer %T", value)
 		return unmarshalErrorf("can not unmarshal into non-pointer %T", value)
@@ -997,24 +1016,24 @@ func unmarshalMap(info *TypeInfo, data []byte, value interface{}) error {
 	if len(data) < 2 {
 	if len(data) < 2 {
 		return unmarshalErrorf("unmarshal map: unexpected eof")
 		return unmarshalErrorf("unmarshal map: unexpected eof")
 	}
 	}
-	n, p := readCollectionSize(info, data)
+	n, p := readCollectionSize(mapInfo, data)
 	data = data[p:]
 	data = data[p:]
 	for i := 0; i < n; i++ {
 	for i := 0; i < n; i++ {
 		if len(data) < 2 {
 		if len(data) < 2 {
 			return unmarshalErrorf("unmarshal list: unexpected eof")
 			return unmarshalErrorf("unmarshal list: unexpected eof")
 		}
 		}
-		m, p := readCollectionSize(info, data)
+		m, p := readCollectionSize(mapInfo, data)
 		data = data[p:]
 		data = data[p:]
 		key := reflect.New(t.Key())
 		key := reflect.New(t.Key())
-		if err := Unmarshal(info.Key, data[:m], key.Interface()); err != nil {
+		if err := Unmarshal(mapInfo.Key, data[:m], key.Interface()); err != nil {
 			return err
 			return err
 		}
 		}
 		data = data[m:]
 		data = data[m:]
 
 
-		m, p = readCollectionSize(info, data)
+		m, p = readCollectionSize(mapInfo, data)
 		data = data[p:]
 		data = data[p:]
 		val := reflect.New(t.Elem())
 		val := reflect.New(t.Elem())
-		if err := Unmarshal(info.Elem, data[:m], val.Interface()); err != nil {
+		if err := Unmarshal(mapInfo.Elem, data[:m], val.Interface()); err != nil {
 			return err
 			return err
 		}
 		}
 		data = data[m:]
 		data = data[m:]
@@ -1024,7 +1043,7 @@ func unmarshalMap(info *TypeInfo, data []byte, value interface{}) error {
 	return nil
 	return nil
 }
 }
 
 
-func marshalUUID(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalUUID(info TypeInfo, value interface{}) ([]byte, error) {
 	switch val := value.(type) {
 	switch val := value.(type) {
 	case UUID:
 	case UUID:
 		return val.Bytes(), nil
 		return val.Bytes(), nil
@@ -1042,7 +1061,7 @@ func marshalUUID(info *TypeInfo, value interface{}) ([]byte, error) {
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 }
 }
 
 
-func unmarshalUUID(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalUUID(info TypeInfo, data []byte, value interface{}) error {
 	if data == nil || len(data) == 0 {
 	if data == nil || len(data) == 0 {
 		switch v := value.(type) {
 		switch v := value.(type) {
 		case *string:
 		case *string:
@@ -1077,7 +1096,7 @@ func unmarshalUUID(info *TypeInfo, data []byte, value interface{}) error {
 	return unmarshalErrorf("can not unmarshal X %s into %T", info, value)
 	return unmarshalErrorf("can not unmarshal X %s into %T", info, value)
 }
 }
 
 
-func unmarshalTimeUUID(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalTimeUUID(info TypeInfo, data []byte, value interface{}) error {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Unmarshaler:
 	case Unmarshaler:
 		return v.UnmarshalCQL(info, data)
 		return v.UnmarshalCQL(info, data)
@@ -1095,7 +1114,7 @@ func unmarshalTimeUUID(info *TypeInfo, data []byte, value interface{}) error {
 	}
 	}
 }
 }
 
 
-func marshalInet(info *TypeInfo, value interface{}) ([]byte, error) {
+func marshalInet(info TypeInfo, value interface{}) ([]byte, error) {
 	// we return either the 4 or 16 byte representation of an
 	// we return either the 4 or 16 byte representation of an
 	// ip address here otherwise the db value will be prefixed
 	// ip address here otherwise the db value will be prefixed
 	// with the remaining byte values e.g. ::ffff:127.0.0.1 and not 127.0.0.1
 	// with the remaining byte values e.g. ::ffff:127.0.0.1 and not 127.0.0.1
@@ -1120,7 +1139,7 @@ func marshalInet(info *TypeInfo, value interface{}) ([]byte, error) {
 	return nil, marshalErrorf("cannot marshal %T into %s", value, info)
 	return nil, marshalErrorf("cannot marshal %T into %s", value, info)
 }
 }
 
 
-func unmarshalInet(info *TypeInfo, data []byte, value interface{}) error {
+func unmarshalInet(info TypeInfo, data []byte, value interface{}) error {
 	switch v := value.(type) {
 	switch v := value.(type) {
 	case Unmarshaler:
 	case Unmarshaler:
 		return v.UnmarshalCQL(info, data)
 		return v.UnmarshalCQL(info, data)
@@ -1149,28 +1168,77 @@ func unmarshalInet(info *TypeInfo, data []byte, value interface{}) error {
 }
 }
 
 
 // TypeInfo describes a Cassandra specific data type.
 // TypeInfo describes a Cassandra specific data type.
-type TypeInfo struct {
-	Proto  byte // version of the protocol
-	Type   Type
-	Key    *TypeInfo // only used for TypeMap
-	Elem   *TypeInfo // only used for TypeMap, TypeList and TypeSet
-	Custom string    // only used for TypeCustom
+type TypeInfo interface {
+	Type() Type
+	Version() byte
+	Custom() string
+
+	// New creates a pointer to an empty version of whatever type
+	// is referenced by the TypeInfo receiver
+	New() interface{}
 }
 }
 
 
-// String returns a human readable name for the Cassandra datatype
-// described by t.
-func (t TypeInfo) String() string {
-	switch t.Type {
+type NativeType struct {
+	proto  byte
+	typ    Type
+	custom string // only used for TypeCustom
+}
+
+func (t NativeType) New() interface{} {
+	return reflect.New(goType(t)).Interface()
+}
+
+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
+}
+
+func (t CollectionType) New() interface{} {
+	return reflect.New(goType(t)).Interface()
+}
+
+func (c CollectionType) String() string {
+	switch c.typ {
 	case TypeMap:
 	case TypeMap:
-		return fmt.Sprintf("%s(%s, %s)", t.Type, t.Key, t.Elem)
+		return fmt.Sprintf("%s(%s, %s)", c.typ, c.Key, c.Elem)
 	case TypeList, TypeSet:
 	case TypeList, TypeSet:
-		return fmt.Sprintf("%s(%s)", t.Type, t.Elem)
+		return fmt.Sprintf("%s(%s)", c.typ, c.Elem)
 	case TypeCustom:
 	case TypeCustom:
-		return fmt.Sprintf("%s(%s)", t.Type, t.Custom)
+		return fmt.Sprintf("%s(%s)", c.typ, c.Custom)
+	default:
+		return c.typ.String()
 	}
 	}
-	return t.Type.String()
 }
 }
 
 
+type TupleTypeInfo struct {
+	NativeType
+	Elem []TypeInfo
+}
+
+// String returns a human readable name for the Cassandra datatype
+// described by t.
 // Type is the identifier of a Cassandra internal datatype.
 // Type is the identifier of a Cassandra internal datatype.
 type Type int
 type Type int
 
 
@@ -1194,6 +1262,8 @@ const (
 	TypeList           = 0x0020
 	TypeList           = 0x0020
 	TypeMap            = 0x0021
 	TypeMap            = 0x0021
 	TypeSet            = 0x0022
 	TypeSet            = 0x0022
+	TypeUDT            = 0x0030
+	TypeTuple          = 0x0031
 )
 )
 
 
 // String returns the name of the identifier.
 // String returns the name of the identifier.

+ 126 - 100
marshal_test.go

@@ -16,47 +16,47 @@ import (
 )
 )
 
 
 var marshalTests = []struct {
 var marshalTests = []struct {
-	Info  *TypeInfo
+	Info  TypeInfo
 	Data  []byte
 	Data  []byte
 	Value interface{}
 	Value interface{}
 }{
 }{
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarchar},
+		NativeType{proto: 2, typ: TypeVarchar},
 		[]byte("hello world"),
 		[]byte("hello world"),
 		[]byte("hello world"),
 		[]byte("hello world"),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarchar},
+		NativeType{proto: 2, typ: TypeVarchar},
 		[]byte("hello world"),
 		[]byte("hello world"),
 		"hello world",
 		"hello world",
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarchar},
+		NativeType{proto: 2, typ: TypeVarchar},
 		[]byte(nil),
 		[]byte(nil),
 		[]byte(nil),
 		[]byte(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarchar},
+		NativeType{proto: 2, typ: TypeVarchar},
 		[]byte("hello world"),
 		[]byte("hello world"),
 		MyString("hello world"),
 		MyString("hello world"),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarchar},
+		NativeType{proto: 2, typ: TypeVarchar},
 		[]byte("HELLO WORLD"),
 		[]byte("HELLO WORLD"),
 		CustomString("hello world"),
 		CustomString("hello world"),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBlob},
+		NativeType{proto: 2, typ: TypeBlob},
 		[]byte("hello\x00"),
 		[]byte("hello\x00"),
 		[]byte("hello\x00"),
 		[]byte("hello\x00"),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBlob},
+		NativeType{proto: 2, typ: TypeBlob},
 		[]byte(nil),
 		[]byte(nil),
 		[]byte(nil),
 		[]byte(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeTimeUUID},
+		NativeType{proto: 2, typ: TypeTimeUUID},
 		[]byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0},
 		[]byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0},
 		func() UUID {
 		func() UUID {
 			x, _ := UUIDFromBytes([]byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0})
 			x, _ := UUIDFromBytes([]byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0})
@@ -64,217 +64,235 @@ var marshalTests = []struct {
 		}(),
 		}(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInt},
+		NativeType{proto: 2, typ: TypeInt},
 		[]byte("\x00\x00\x00\x00"),
 		[]byte("\x00\x00\x00\x00"),
 		0,
 		0,
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInt},
+		NativeType{proto: 2, typ: TypeInt},
 		[]byte("\x01\x02\x03\x04"),
 		[]byte("\x01\x02\x03\x04"),
 		int(16909060),
 		int(16909060),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInt},
+		NativeType{proto: 2, typ: TypeInt},
 		[]byte("\x80\x00\x00\x00"),
 		[]byte("\x80\x00\x00\x00"),
 		int32(math.MinInt32),
 		int32(math.MinInt32),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInt},
+		NativeType{proto: 2, typ: TypeInt},
 		[]byte("\x7f\xff\xff\xff"),
 		[]byte("\x7f\xff\xff\xff"),
 		int32(math.MaxInt32),
 		int32(math.MaxInt32),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInt},
+		NativeType{proto: 2, typ: TypeInt},
 		[]byte("\x00\x00\x00\x00"),
 		[]byte("\x00\x00\x00\x00"),
 		"0",
 		"0",
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInt},
+		NativeType{proto: 2, typ: TypeInt},
 		[]byte("\x01\x02\x03\x04"),
 		[]byte("\x01\x02\x03\x04"),
 		"16909060",
 		"16909060",
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInt},
+		NativeType{proto: 2, typ: TypeInt},
 		[]byte("\x80\x00\x00\x00"),
 		[]byte("\x80\x00\x00\x00"),
 		"-2147483648", // math.MinInt32
 		"-2147483648", // math.MinInt32
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInt},
+		NativeType{proto: 2, typ: TypeInt},
 		[]byte("\x7f\xff\xff\xff"),
 		[]byte("\x7f\xff\xff\xff"),
 		"2147483647", // math.MaxInt32
 		"2147483647", // math.MaxInt32
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBigInt},
+		NativeType{proto: 2, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\x00\x00\x00\x00"),
 		[]byte("\x00\x00\x00\x00\x00\x00\x00\x00"),
 		0,
 		0,
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBigInt},
+		NativeType{proto: 2, typ: TypeBigInt},
 		[]byte("\x01\x02\x03\x04\x05\x06\x07\x08"),
 		[]byte("\x01\x02\x03\x04\x05\x06\x07\x08"),
 		72623859790382856,
 		72623859790382856,
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBigInt},
+		NativeType{proto: 2, typ: TypeBigInt},
 		[]byte("\x80\x00\x00\x00\x00\x00\x00\x00"),
 		[]byte("\x80\x00\x00\x00\x00\x00\x00\x00"),
 		int64(math.MinInt64),
 		int64(math.MinInt64),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBigInt},
+		NativeType{proto: 2, typ: TypeBigInt},
 		[]byte("\x7f\xff\xff\xff\xff\xff\xff\xff"),
 		[]byte("\x7f\xff\xff\xff\xff\xff\xff\xff"),
 		int64(math.MaxInt64),
 		int64(math.MaxInt64),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBigInt},
+		NativeType{proto: 2, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\x00\x00\x00\x00"),
 		[]byte("\x00\x00\x00\x00\x00\x00\x00\x00"),
 		"0",
 		"0",
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBigInt},
+		NativeType{proto: 2, typ: TypeBigInt},
 		[]byte("\x01\x02\x03\x04\x05\x06\x07\x08"),
 		[]byte("\x01\x02\x03\x04\x05\x06\x07\x08"),
 		"72623859790382856",
 		"72623859790382856",
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBigInt},
+		NativeType{proto: 2, typ: TypeBigInt},
 		[]byte("\x80\x00\x00\x00\x00\x00\x00\x00"),
 		[]byte("\x80\x00\x00\x00\x00\x00\x00\x00"),
 		"-9223372036854775808", // math.MinInt64
 		"-9223372036854775808", // math.MinInt64
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBigInt},
+		NativeType{proto: 2, typ: TypeBigInt},
 		[]byte("\x7f\xff\xff\xff\xff\xff\xff\xff"),
 		[]byte("\x7f\xff\xff\xff\xff\xff\xff\xff"),
 		"9223372036854775807", // math.MaxInt64
 		"9223372036854775807", // math.MaxInt64
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBoolean},
+		NativeType{proto: 2, typ: TypeBoolean},
 		[]byte("\x00"),
 		[]byte("\x00"),
 		false,
 		false,
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBoolean},
+		NativeType{proto: 2, typ: TypeBoolean},
 		[]byte("\x01"),
 		[]byte("\x01"),
 		true,
 		true,
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeFloat},
+		NativeType{proto: 2, typ: TypeFloat},
 		[]byte("\x40\x49\x0f\xdb"),
 		[]byte("\x40\x49\x0f\xdb"),
 		float32(3.14159265),
 		float32(3.14159265),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDouble},
+		NativeType{proto: 2, typ: TypeDouble},
 		[]byte("\x40\x09\x21\xfb\x53\xc8\xd4\xf1"),
 		[]byte("\x40\x09\x21\xfb\x53\xc8\xd4\xf1"),
 		float64(3.14159265),
 		float64(3.14159265),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDecimal},
+		NativeType{proto: 2, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x00\x00"),
 		[]byte("\x00\x00\x00\x00\x00"),
 		inf.NewDec(0, 0),
 		inf.NewDec(0, 0),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDecimal},
+		NativeType{proto: 2, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x00\x64"),
 		[]byte("\x00\x00\x00\x00\x64"),
 		inf.NewDec(100, 0),
 		inf.NewDec(100, 0),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDecimal},
+		NativeType{proto: 2, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x02\x19"),
 		[]byte("\x00\x00\x00\x02\x19"),
 		decimalize("0.25"),
 		decimalize("0.25"),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDecimal},
+		NativeType{proto: 2, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x13\xD5\a;\x20\x14\xA2\x91"),
 		[]byte("\x00\x00\x00\x13\xD5\a;\x20\x14\xA2\x91"),
 		decimalize("-0.0012095473475870063"), // From the iconara/cql-rb test suite
 		decimalize("-0.0012095473475870063"), // From the iconara/cql-rb test suite
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDecimal},
+		NativeType{proto: 2, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x13*\xF8\xC4\xDF\xEB]o"),
 		[]byte("\x00\x00\x00\x13*\xF8\xC4\xDF\xEB]o"),
 		decimalize("0.0012095473475870063"), // From the iconara/cql-rb test suite
 		decimalize("0.0012095473475870063"), // From the iconara/cql-rb test suite
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDecimal},
+		NativeType{proto: 2, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x12\xF2\xD8\x02\xB6R\x7F\x99\xEE\x98#\x99\xA9V"),
 		[]byte("\x00\x00\x00\x12\xF2\xD8\x02\xB6R\x7F\x99\xEE\x98#\x99\xA9V"),
 		decimalize("-1042342234234.123423435647768234"), // From the iconara/cql-rb test suite
 		decimalize("-1042342234234.123423435647768234"), // From the iconara/cql-rb test suite
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDecimal},
+		NativeType{proto: 2, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\r\nJ\x04\"^\x91\x04\x8a\xb1\x18\xfe"),
 		[]byte("\x00\x00\x00\r\nJ\x04\"^\x91\x04\x8a\xb1\x18\xfe"),
 		decimalize("1243878957943.1234124191998"), // From the datastax/python-driver test suite
 		decimalize("1243878957943.1234124191998"), // From the datastax/python-driver test suite
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDecimal},
+		NativeType{proto: 2, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x06\xe5\xde]\x98Y"),
 		[]byte("\x00\x00\x00\x06\xe5\xde]\x98Y"),
 		decimalize("-112233.441191"), // From the datastax/python-driver test suite
 		decimalize("-112233.441191"), // From the datastax/python-driver test suite
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDecimal},
+		NativeType{proto: 2, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x14\x00\xfa\xce"),
 		[]byte("\x00\x00\x00\x14\x00\xfa\xce"),
 		decimalize("0.00000000000000064206"), // From the datastax/python-driver test suite
 		decimalize("0.00000000000000064206"), // From the datastax/python-driver test suite
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDecimal},
+		NativeType{proto: 2, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x14\xff\x052"),
 		[]byte("\x00\x00\x00\x14\xff\x052"),
 		decimalize("-0.00000000000000064206"), // From the datastax/python-driver test suite
 		decimalize("-0.00000000000000064206"), // From the datastax/python-driver test suite
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDecimal},
+		NativeType{proto: 2, typ: TypeDecimal},
 		[]byte("\xff\xff\xff\x9c\x00\xfa\xce"),
 		[]byte("\xff\xff\xff\x9c\x00\xfa\xce"),
 		inf.NewDec(64206, -100), // From the datastax/python-driver test suite
 		inf.NewDec(64206, -100), // From the datastax/python-driver test suite
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeTimestamp},
+		NativeType{proto: 2, typ: TypeTimestamp},
 		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 		time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC),
 		time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeTimestamp},
+		NativeType{proto: 2, typ: TypeTimestamp},
 		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 		int64(1376387523000),
 		int64(1376387523000),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeList, Elem: &TypeInfo{Proto: 2, Type: TypeInt}},
+		CollectionType{
+			NativeType: NativeType{proto: 2, typ: TypeList},
+			Elem:       NativeType{proto: 2, typ: TypeInt},
+		},
 		[]byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"),
 		[]byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"),
 		[]int{1, 2},
 		[]int{1, 2},
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeList, Elem: &TypeInfo{Proto: 2, Type: TypeInt}},
+		CollectionType{
+			NativeType: NativeType{proto: 2, typ: TypeList},
+			Elem:       NativeType{proto: 2, typ: TypeInt},
+		},
 		[]byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"),
 		[]byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"),
 		[2]int{1, 2},
 		[2]int{1, 2},
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeSet, Elem: &TypeInfo{Proto: 2, Type: TypeInt}},
+		CollectionType{
+			NativeType: NativeType{proto: 2, typ: TypeSet},
+			Elem:       NativeType{proto: 2, typ: TypeInt},
+		},
 		[]byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"),
 		[]byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"),
 		[]int{1, 2},
 		[]int{1, 2},
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeSet, Elem: &TypeInfo{Proto: 2, Type: TypeInt}},
+		CollectionType{
+			NativeType: NativeType{proto: 2, typ: TypeSet},
+			Elem:       NativeType{proto: 2, typ: TypeInt},
+		},
 		[]byte(nil),
 		[]byte(nil),
 		[]int(nil),
 		[]int(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeMap,
-			Key:  &TypeInfo{Proto: 2, Type: TypeVarchar},
-			Elem: &TypeInfo{Proto: 2, Type: TypeInt},
+		CollectionType{
+			NativeType: NativeType{proto: 2, typ: TypeMap},
+			Key:        NativeType{proto: 2, typ: TypeVarchar},
+			Elem:       NativeType{proto: 2, typ: TypeInt},
 		},
 		},
 		[]byte("\x00\x01\x00\x03foo\x00\x04\x00\x00\x00\x01"),
 		[]byte("\x00\x01\x00\x03foo\x00\x04\x00\x00\x00\x01"),
 		map[string]int{"foo": 1},
 		map[string]int{"foo": 1},
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeMap,
-			Key:  &TypeInfo{Proto: 2, Type: TypeVarchar},
-			Elem: &TypeInfo{Proto: 2, Type: TypeInt},
+		CollectionType{
+			NativeType: NativeType{proto: 2, typ: TypeMap},
+			Key:        NativeType{proto: 2, typ: TypeVarchar},
+			Elem:       NativeType{proto: 2, typ: TypeInt},
 		},
 		},
 		[]byte(nil),
 		[]byte(nil),
 		map[string]int(nil),
 		map[string]int(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeList, Elem: &TypeInfo{Proto: 2, Type: TypeVarchar}},
+		CollectionType{
+			NativeType: NativeType{proto: 2, typ: TypeList},
+			Elem:       NativeType{proto: 2, typ: TypeVarchar},
+		},
 		bytes.Join([][]byte{
 		bytes.Join([][]byte{
 			[]byte("\x00\x01\xFF\xFF"),
 			[]byte("\x00\x01\xFF\xFF"),
 			bytes.Repeat([]byte("X"), 65535)}, []byte("")),
 			bytes.Repeat([]byte("X"), 65535)}, []byte("")),
 		[]string{strings.Repeat("X", 65535)},
 		[]string{strings.Repeat("X", 65535)},
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeMap,
-			Key:  &TypeInfo{Proto: 2, Type: TypeVarchar},
-			Elem: &TypeInfo{Proto: 2, Type: TypeVarchar},
+		CollectionType{
+			NativeType: NativeType{proto: 2, typ: TypeMap},
+			Key:        NativeType{proto: 2, typ: TypeVarchar},
+			Elem:       NativeType{proto: 2, typ: TypeVarchar},
 		},
 		},
 		bytes.Join([][]byte{
 		bytes.Join([][]byte{
 			[]byte("\x00\x01\xFF\xFF"),
 			[]byte("\x00\x01\xFF\xFF"),
@@ -286,82 +304,82 @@ var marshalTests = []struct {
 		},
 		},
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarint},
+		NativeType{proto: 2, typ: TypeVarint},
 		[]byte("\x00"),
 		[]byte("\x00"),
 		0,
 		0,
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarint},
+		NativeType{proto: 2, typ: TypeVarint},
 		[]byte("\x37\xE2\x3C\xEC"),
 		[]byte("\x37\xE2\x3C\xEC"),
 		int32(937573612),
 		int32(937573612),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarint},
+		NativeType{proto: 2, typ: TypeVarint},
 		[]byte("\x37\xE2\x3C\xEC"),
 		[]byte("\x37\xE2\x3C\xEC"),
 		big.NewInt(937573612),
 		big.NewInt(937573612),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarint},
+		NativeType{proto: 2, typ: TypeVarint},
 		[]byte("\x03\x9EV \x15\f\x03\x9DK\x18\xCDI\\$?\a["),
 		[]byte("\x03\x9EV \x15\f\x03\x9DK\x18\xCDI\\$?\a["),
 		bigintize("1231312312331283012830129382342342412123"), // From the iconara/cql-rb test suite
 		bigintize("1231312312331283012830129382342342412123"), // From the iconara/cql-rb test suite
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarint},
+		NativeType{proto: 2, typ: TypeVarint},
 		[]byte("\xC9v\x8D:\x86"),
 		[]byte("\xC9v\x8D:\x86"),
 		big.NewInt(-234234234234), // From the iconara/cql-rb test suite
 		big.NewInt(-234234234234), // From the iconara/cql-rb test suite
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarint},
+		NativeType{proto: 2, typ: TypeVarint},
 		[]byte("f\x1e\xfd\xf2\xe3\xb1\x9f|\x04_\x15"),
 		[]byte("f\x1e\xfd\xf2\xe3\xb1\x9f|\x04_\x15"),
 		bigintize("123456789123456789123456789"), // From the datastax/python-driver test suite
 		bigintize("123456789123456789123456789"), // From the datastax/python-driver test suite
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInet},
+		NativeType{proto: 2, typ: TypeInet},
 		[]byte("\x7F\x00\x00\x01"),
 		[]byte("\x7F\x00\x00\x01"),
 		net.ParseIP("127.0.0.1").To4(),
 		net.ParseIP("127.0.0.1").To4(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInet},
+		NativeType{proto: 2, typ: TypeInet},
 		[]byte("\xFF\xFF\xFF\xFF"),
 		[]byte("\xFF\xFF\xFF\xFF"),
 		net.ParseIP("255.255.255.255").To4(),
 		net.ParseIP("255.255.255.255").To4(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInet},
+		NativeType{proto: 2, typ: TypeInet},
 		[]byte("\x7F\x00\x00\x01"),
 		[]byte("\x7F\x00\x00\x01"),
 		"127.0.0.1",
 		"127.0.0.1",
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInet},
+		NativeType{proto: 2, typ: TypeInet},
 		[]byte("\xFF\xFF\xFF\xFF"),
 		[]byte("\xFF\xFF\xFF\xFF"),
 		"255.255.255.255",
 		"255.255.255.255",
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInet},
+		NativeType{proto: 2, typ: TypeInet},
 		[]byte("\x21\xDA\x00\xd3\x00\x00\x2f\x3b\x02\xaa\x00\xff\xfe\x28\x9c\x5a"),
 		[]byte("\x21\xDA\x00\xd3\x00\x00\x2f\x3b\x02\xaa\x00\xff\xfe\x28\x9c\x5a"),
 		"21da:d3:0:2f3b:2aa:ff:fe28:9c5a",
 		"21da:d3:0:2f3b:2aa:ff:fe28:9c5a",
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInet},
+		NativeType{proto: 2, typ: TypeInet},
 		[]byte("\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x02\xb3\xff\xfe\x1e\x83\x29"),
 		[]byte("\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x02\xb3\xff\xfe\x1e\x83\x29"),
 		"fe80::202:b3ff:fe1e:8329",
 		"fe80::202:b3ff:fe1e:8329",
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInet},
+		NativeType{proto: 2, typ: TypeInet},
 		[]byte("\x21\xDA\x00\xd3\x00\x00\x2f\x3b\x02\xaa\x00\xff\xfe\x28\x9c\x5a"),
 		[]byte("\x21\xDA\x00\xd3\x00\x00\x2f\x3b\x02\xaa\x00\xff\xfe\x28\x9c\x5a"),
 		net.ParseIP("21da:d3:0:2f3b:2aa:ff:fe28:9c5a"),
 		net.ParseIP("21da:d3:0:2f3b:2aa:ff:fe28:9c5a"),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInet},
+		NativeType{proto: 2, typ: TypeInet},
 		[]byte("\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x02\xb3\xff\xfe\x1e\x83\x29"),
 		[]byte("\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x02\xb3\xff\xfe\x1e\x83\x29"),
 		net.ParseIP("fe80::202:b3ff:fe1e:8329"),
 		net.ParseIP("fe80::202:b3ff:fe1e:8329"),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInt},
+		NativeType{proto: 2, typ: TypeInt},
 		[]byte(nil),
 		[]byte(nil),
 		nil,
 		nil,
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarchar},
+		NativeType{proto: 2, typ: TypeVarchar},
 		[]byte("nullable string"),
 		[]byte("nullable string"),
 		func() *string {
 		func() *string {
 			value := "nullable string"
 			value := "nullable string"
@@ -369,12 +387,12 @@ var marshalTests = []struct {
 		}(),
 		}(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeVarchar},
+		NativeType{proto: 2, typ: TypeVarchar},
 		[]byte{},
 		[]byte{},
 		(*string)(nil),
 		(*string)(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInt},
+		NativeType{proto: 2, typ: TypeInt},
 		[]byte("\x7f\xff\xff\xff"),
 		[]byte("\x7f\xff\xff\xff"),
 		func() *int {
 		func() *int {
 			var value int = math.MaxInt32
 			var value int = math.MaxInt32
@@ -382,22 +400,22 @@ var marshalTests = []struct {
 		}(),
 		}(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInt},
+		NativeType{proto: 2, typ: TypeInt},
 		[]byte(nil),
 		[]byte(nil),
 		(*int)(nil),
 		(*int)(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeTimeUUID},
+		NativeType{proto: 2, typ: TypeTimeUUID},
 		[]byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0},
 		[]byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0},
 		&UUID{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0},
 		&UUID{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0},
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeTimeUUID},
+		NativeType{proto: 2, typ: TypeTimeUUID},
 		[]byte{},
 		[]byte{},
 		(*UUID)(nil),
 		(*UUID)(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeTimestamp},
+		NativeType{proto: 2, typ: TypeTimestamp},
 		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 		func() *time.Time {
 		func() *time.Time {
 			t := time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC)
 			t := time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC)
@@ -405,12 +423,12 @@ var marshalTests = []struct {
 		}(),
 		}(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeTimestamp},
+		NativeType{proto: 2, typ: TypeTimestamp},
 		[]byte(nil),
 		[]byte(nil),
 		(*time.Time)(nil),
 		(*time.Time)(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBoolean},
+		NativeType{proto: 2, typ: TypeBoolean},
 		[]byte("\x00"),
 		[]byte("\x00"),
 		func() *bool {
 		func() *bool {
 			b := false
 			b := false
@@ -418,7 +436,7 @@ var marshalTests = []struct {
 		}(),
 		}(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBoolean},
+		NativeType{proto: 2, typ: TypeBoolean},
 		[]byte("\x01"),
 		[]byte("\x01"),
 		func() *bool {
 		func() *bool {
 			b := true
 			b := true
@@ -426,12 +444,12 @@ var marshalTests = []struct {
 		}(),
 		}(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeBoolean},
+		NativeType{proto: 2, typ: TypeBoolean},
 		[]byte(nil),
 		[]byte(nil),
 		(*bool)(nil),
 		(*bool)(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeFloat},
+		NativeType{proto: 2, typ: TypeFloat},
 		[]byte("\x40\x49\x0f\xdb"),
 		[]byte("\x40\x49\x0f\xdb"),
 		func() *float32 {
 		func() *float32 {
 			f := float32(3.14159265)
 			f := float32(3.14159265)
@@ -439,12 +457,12 @@ var marshalTests = []struct {
 		}(),
 		}(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeFloat},
+		NativeType{proto: 2, typ: TypeFloat},
 		[]byte(nil),
 		[]byte(nil),
 		(*float32)(nil),
 		(*float32)(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDouble},
+		NativeType{proto: 2, typ: TypeDouble},
 		[]byte("\x40\x09\x21\xfb\x53\xc8\xd4\xf1"),
 		[]byte("\x40\x09\x21\xfb\x53\xc8\xd4\xf1"),
 		func() *float64 {
 		func() *float64 {
 			d := float64(3.14159265)
 			d := float64(3.14159265)
@@ -452,12 +470,12 @@ var marshalTests = []struct {
 		}(),
 		}(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeDouble},
+		NativeType{proto: 2, typ: TypeDouble},
 		[]byte(nil),
 		[]byte(nil),
 		(*float64)(nil),
 		(*float64)(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInet},
+		NativeType{proto: 2, typ: TypeInet},
 		[]byte("\x7F\x00\x00\x01"),
 		[]byte("\x7F\x00\x00\x01"),
 		func() *net.IP {
 		func() *net.IP {
 			ip := net.ParseIP("127.0.0.1").To4()
 			ip := net.ParseIP("127.0.0.1").To4()
@@ -465,12 +483,15 @@ var marshalTests = []struct {
 		}(),
 		}(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeInet},
+		NativeType{proto: 2, typ: TypeInet},
 		[]byte(nil),
 		[]byte(nil),
 		(*net.IP)(nil),
 		(*net.IP)(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeList, Elem: &TypeInfo{Proto: 2, Type: TypeInt}},
+		CollectionType{
+			NativeType: NativeType{proto: 2, typ: TypeList},
+			Elem:       NativeType{proto: 2, typ: TypeInt},
+		},
 		[]byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"),
 		[]byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"),
 		func() *[]int {
 		func() *[]int {
 			l := []int{1, 2}
 			l := []int{1, 2}
@@ -478,14 +499,18 @@ var marshalTests = []struct {
 		}(),
 		}(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeList, Elem: &TypeInfo{Proto: 2, Type: TypeInt}},
+		CollectionType{
+			NativeType: NativeType{proto: 2, typ: TypeList},
+			Elem:       NativeType{proto: 2, typ: TypeInt},
+		},
 		[]byte(nil),
 		[]byte(nil),
 		(*[]int)(nil),
 		(*[]int)(nil),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeMap,
-			Key:  &TypeInfo{Proto: 2, Type: TypeVarchar},
-			Elem: &TypeInfo{Proto: 2, Type: TypeInt},
+		CollectionType{
+			NativeType: NativeType{proto: 2, typ: TypeMap},
+			Key:        NativeType{proto: 2, typ: TypeVarchar},
+			Elem:       NativeType{proto: 2, typ: TypeInt},
 		},
 		},
 		[]byte("\x00\x01\x00\x03foo\x00\x04\x00\x00\x00\x01"),
 		[]byte("\x00\x01\x00\x03foo\x00\x04\x00\x00\x00\x01"),
 		func() *map[string]int {
 		func() *map[string]int {
@@ -494,9 +519,10 @@ var marshalTests = []struct {
 		}(),
 		}(),
 	},
 	},
 	{
 	{
-		&TypeInfo{Proto: 2, Type: TypeMap,
-			Key:  &TypeInfo{Proto: 2, Type: TypeVarchar},
-			Elem: &TypeInfo{Proto: 2, Type: TypeInt},
+		CollectionType{
+			NativeType: NativeType{proto: 2, typ: TypeMap},
+			Key:        NativeType{proto: 2, typ: TypeVarchar},
+			Elem:       NativeType{proto: 2, typ: TypeInt},
 		},
 		},
 		[]byte(nil),
 		[]byte(nil),
 		(*map[string]int)(nil),
 		(*map[string]int)(nil),
@@ -610,7 +636,7 @@ func TestMarshalVarint(t *testing.T) {
 	}
 	}
 
 
 	for i, test := range varintTests {
 	for i, test := range varintTests {
-		data, err := Marshal(&TypeInfo{Proto: 2, Type: TypeVarint}, test.Value)
+		data, err := Marshal(NativeType{proto: 2, typ: TypeVarint}, test.Value)
 		if err != nil {
 		if err != nil {
 			t.Errorf("error marshaling varint: %v (test #%d)", err, i)
 			t.Errorf("error marshaling varint: %v (test #%d)", err, i)
 		}
 		}
@@ -620,7 +646,7 @@ func TestMarshalVarint(t *testing.T) {
 		}
 		}
 
 
 		binder := new(big.Int)
 		binder := new(big.Int)
-		err = Unmarshal(&TypeInfo{Proto: 2, Type: TypeVarint}, test.Marshaled, binder)
+		err = Unmarshal(NativeType{proto: 2, typ: TypeVarint}, test.Marshaled, binder)
 		if err != nil {
 		if err != nil {
 			t.Errorf("error unmarshaling varint: %v (test #%d)", err, i)
 			t.Errorf("error unmarshaling varint: %v (test #%d)", err, i)
 		}
 		}
@@ -633,10 +659,10 @@ func TestMarshalVarint(t *testing.T) {
 
 
 type CustomString string
 type CustomString string
 
 
-func (c CustomString) MarshalCQL(info *TypeInfo) ([]byte, error) {
+func (c CustomString) MarshalCQL(info TypeInfo) ([]byte, error) {
 	return []byte(strings.ToUpper(string(c))), nil
 	return []byte(strings.ToUpper(string(c))), nil
 }
 }
-func (c *CustomString) UnmarshalCQL(info *TypeInfo, data []byte) error {
+func (c *CustomString) UnmarshalCQL(info TypeInfo, data []byte) error {
 	*c = CustomString(strings.ToLower(string(data)))
 	*c = CustomString(strings.ToLower(string(data)))
 	return nil
 	return nil
 }
 }

+ 23 - 17
metadata.go

@@ -227,7 +227,7 @@ func compileV1Metadata(tables []TableMetadata) {
 		if comparatorParsed.isComposite {
 		if comparatorParsed.isComposite {
 			if len(comparatorParsed.collections) != 0 ||
 			if len(comparatorParsed.collections) != 0 ||
 				(len(table.ColumnAliases) == size-1 &&
 				(len(table.ColumnAliases) == size-1 &&
-					comparatorParsed.types[size-1].Type == TypeVarchar) {
+					comparatorParsed.types[size-1].Type() == TypeVarchar) {
 				size = size - 1
 				size = size - 1
 			}
 			}
 		} else {
 		} else {
@@ -603,9 +603,9 @@ func (t *typeParser) parse() typeParserResult {
 		return typeParserResult{
 		return typeParserResult{
 			isComposite: false,
 			isComposite: false,
 			types: []TypeInfo{
 			types: []TypeInfo{
-				TypeInfo{
-					Type:   TypeCustom,
-					Custom: t.input,
+				NativeType{
+					typ:    TypeCustom,
+					custom: t.input,
 				},
 				},
 			},
 			},
 			reversed:    []bool{false},
 			reversed:    []bool{false},
@@ -681,33 +681,39 @@ func (t *typeParser) parse() typeParserResult {
 func (class *typeParserClassNode) asTypeInfo() TypeInfo {
 func (class *typeParserClassNode) asTypeInfo() TypeInfo {
 	if strings.HasPrefix(class.name, LIST_TYPE) {
 	if strings.HasPrefix(class.name, LIST_TYPE) {
 		elem := class.params[0].class.asTypeInfo()
 		elem := class.params[0].class.asTypeInfo()
-		return TypeInfo{
-			Type: TypeList,
-			Elem: &elem,
+		return CollectionType{
+			NativeType: NativeType{
+				typ: TypeList,
+			},
+			Elem: elem,
 		}
 		}
 	}
 	}
 	if strings.HasPrefix(class.name, SET_TYPE) {
 	if strings.HasPrefix(class.name, SET_TYPE) {
 		elem := class.params[0].class.asTypeInfo()
 		elem := class.params[0].class.asTypeInfo()
-		return TypeInfo{
-			Type: TypeSet,
-			Elem: &elem,
+		return CollectionType{
+			NativeType: NativeType{
+				typ: TypeSet,
+			},
+			Elem: elem,
 		}
 		}
 	}
 	}
 	if strings.HasPrefix(class.name, MAP_TYPE) {
 	if strings.HasPrefix(class.name, MAP_TYPE) {
 		key := class.params[0].class.asTypeInfo()
 		key := class.params[0].class.asTypeInfo()
 		elem := class.params[1].class.asTypeInfo()
 		elem := class.params[1].class.asTypeInfo()
-		return TypeInfo{
-			Type: TypeMap,
-			Key:  &key,
-			Elem: &elem,
+		return CollectionType{
+			NativeType: NativeType{
+				typ: TypeMap,
+			},
+			Key:  key,
+			Elem: elem,
 		}
 		}
 	}
 	}
 
 
 	// must be a simple type or custom type
 	// must be a simple type or custom type
-	info := TypeInfo{Type: getApacheCassandraType(class.name)}
-	if info.Type == TypeCustom {
+	info := NativeType{typ: getApacheCassandraType(class.name)}
+	if info.typ == TypeCustom {
 		// add the entire class definition
 		// add the entire class definition
-		info.Custom = class.input
+		info.custom = class.input
 	}
 	}
 	return info
 	return info
 }
 }

+ 73 - 66
metadata_test.go

@@ -92,14 +92,14 @@ func TestCompileMetadata(t *testing.T) {
 					PartitionKey: []*ColumnMetadata{
 					PartitionKey: []*ColumnMetadata{
 						&ColumnMetadata{
 						&ColumnMetadata{
 							Name: "key",
 							Name: "key",
-							Type: TypeInfo{Type: TypeBlob},
+							Type: NativeType{typ: TypeBlob},
 						},
 						},
 					},
 					},
 					ClusteringColumns: []*ColumnMetadata{},
 					ClusteringColumns: []*ColumnMetadata{},
 					Columns: map[string]*ColumnMetadata{
 					Columns: map[string]*ColumnMetadata{
 						"key": &ColumnMetadata{
 						"key": &ColumnMetadata{
 							Name: "key",
 							Name: "key",
-							Type: TypeInfo{Type: TypeBlob},
+							Type: NativeType{typ: TypeBlob},
 							Kind: PARTITION_KEY,
 							Kind: PARTITION_KEY,
 						},
 						},
 					},
 					},
@@ -108,42 +108,42 @@ func TestCompileMetadata(t *testing.T) {
 					PartitionKey: []*ColumnMetadata{
 					PartitionKey: []*ColumnMetadata{
 						&ColumnMetadata{
 						&ColumnMetadata{
 							Name: "target_id",
 							Name: "target_id",
-							Type: TypeInfo{Type: TypeUUID},
+							Type: NativeType{typ: TypeUUID},
 						},
 						},
 					},
 					},
 					ClusteringColumns: []*ColumnMetadata{
 					ClusteringColumns: []*ColumnMetadata{
 						&ColumnMetadata{
 						&ColumnMetadata{
 							Name:  "hint_id",
 							Name:  "hint_id",
-							Type:  TypeInfo{Type: TypeTimeUUID},
+							Type:  NativeType{typ: TypeTimeUUID},
 							Order: ASC,
 							Order: ASC,
 						},
 						},
 						&ColumnMetadata{
 						&ColumnMetadata{
 							Name:  "message_version",
 							Name:  "message_version",
-							Type:  TypeInfo{Type: TypeInt},
+							Type:  NativeType{typ: TypeInt},
 							Order: ASC,
 							Order: ASC,
 						},
 						},
 					},
 					},
 					Columns: map[string]*ColumnMetadata{
 					Columns: map[string]*ColumnMetadata{
 						"target_id": &ColumnMetadata{
 						"target_id": &ColumnMetadata{
 							Name: "target_id",
 							Name: "target_id",
-							Type: TypeInfo{Type: TypeUUID},
+							Type: NativeType{typ: TypeUUID},
 							Kind: PARTITION_KEY,
 							Kind: PARTITION_KEY,
 						},
 						},
 						"hint_id": &ColumnMetadata{
 						"hint_id": &ColumnMetadata{
 							Name:  "hint_id",
 							Name:  "hint_id",
-							Type:  TypeInfo{Type: TypeTimeUUID},
+							Type:  NativeType{typ: TypeTimeUUID},
 							Order: ASC,
 							Order: ASC,
 							Kind:  CLUSTERING_KEY,
 							Kind:  CLUSTERING_KEY,
 						},
 						},
 						"message_version": &ColumnMetadata{
 						"message_version": &ColumnMetadata{
 							Name:  "message_version",
 							Name:  "message_version",
-							Type:  TypeInfo{Type: TypeInt},
+							Type:  NativeType{typ: TypeInt},
 							Order: ASC,
 							Order: ASC,
 							Kind:  CLUSTERING_KEY,
 							Kind:  CLUSTERING_KEY,
 						},
 						},
 						"mutation": &ColumnMetadata{
 						"mutation": &ColumnMetadata{
 							Name: "mutation",
 							Name: "mutation",
-							Type: TypeInfo{Type: TypeBlob},
+							Type: NativeType{typ: TypeBlob},
 							Kind: REGULAR,
 							Kind: REGULAR,
 						},
 						},
 					},
 					},
@@ -152,53 +152,53 @@ func TestCompileMetadata(t *testing.T) {
 					PartitionKey: []*ColumnMetadata{
 					PartitionKey: []*ColumnMetadata{
 						&ColumnMetadata{
 						&ColumnMetadata{
 							Name: "peer",
 							Name: "peer",
-							Type: TypeInfo{Type: TypeInet},
+							Type: NativeType{typ: TypeInet},
 						},
 						},
 					},
 					},
 					ClusteringColumns: []*ColumnMetadata{},
 					ClusteringColumns: []*ColumnMetadata{},
 					Columns: map[string]*ColumnMetadata{
 					Columns: map[string]*ColumnMetadata{
 						"peer": &ColumnMetadata{
 						"peer": &ColumnMetadata{
 							Name: "peer",
 							Name: "peer",
-							Type: TypeInfo{Type: TypeInet},
+							Type: NativeType{typ: TypeInet},
 							Kind: PARTITION_KEY,
 							Kind: PARTITION_KEY,
 						},
 						},
-						"data_center":     &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "data_center", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type", Type: TypeInfo{Type: TypeVarchar}},
-						"host_id":         &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "host_id", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UUIDType", Type: TypeInfo{Type: TypeUUID}},
-						"rack":            &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "rack", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type", Type: TypeInfo{Type: TypeVarchar}},
-						"release_version": &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "release_version", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type", Type: TypeInfo{Type: TypeVarchar}},
-						"rpc_address":     &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "rpc_address", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.InetAddressType", Type: TypeInfo{Type: TypeInet}},
-						"schema_version":  &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "schema_version", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UUIDType", Type: TypeInfo{Type: TypeUUID}},
-						"tokens":          &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "tokens", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.UTF8Type)", Type: TypeInfo{Type: TypeSet}},
+						"data_center":     &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "data_center", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type", Type: NativeType{typ: TypeVarchar}},
+						"host_id":         &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "host_id", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UUIDType", Type: NativeType{typ: TypeUUID}},
+						"rack":            &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "rack", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type", Type: NativeType{typ: TypeVarchar}},
+						"release_version": &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "release_version", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type", Type: NativeType{typ: TypeVarchar}},
+						"rpc_address":     &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "rpc_address", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.InetAddressType", Type: NativeType{typ: TypeInet}},
+						"schema_version":  &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "schema_version", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UUIDType", Type: NativeType{typ: TypeUUID}},
+						"tokens":          &ColumnMetadata{Keyspace: "V1Keyspace", Table: "peers", Kind: REGULAR, Name: "tokens", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.UTF8Type)", Type: CollectionType{NativeType: NativeType{typ: TypeSet}}},
 					},
 					},
 				},
 				},
 				"IndexInfo": &TableMetadata{
 				"IndexInfo": &TableMetadata{
 					PartitionKey: []*ColumnMetadata{
 					PartitionKey: []*ColumnMetadata{
 						&ColumnMetadata{
 						&ColumnMetadata{
 							Name: "table_name",
 							Name: "table_name",
-							Type: TypeInfo{Type: TypeVarchar},
+							Type: NativeType{typ: TypeVarchar},
 						},
 						},
 					},
 					},
 					ClusteringColumns: []*ColumnMetadata{
 					ClusteringColumns: []*ColumnMetadata{
 						&ColumnMetadata{
 						&ColumnMetadata{
 							Name:  "index_name",
 							Name:  "index_name",
-							Type:  TypeInfo{Type: TypeVarchar},
+							Type:  NativeType{typ: TypeVarchar},
 							Order: ASC,
 							Order: ASC,
 						},
 						},
 					},
 					},
 					Columns: map[string]*ColumnMetadata{
 					Columns: map[string]*ColumnMetadata{
 						"table_name": &ColumnMetadata{
 						"table_name": &ColumnMetadata{
 							Name: "table_name",
 							Name: "table_name",
-							Type: TypeInfo{Type: TypeVarchar},
+							Type: NativeType{typ: TypeVarchar},
 							Kind: PARTITION_KEY,
 							Kind: PARTITION_KEY,
 						},
 						},
 						"index_name": &ColumnMetadata{
 						"index_name": &ColumnMetadata{
 							Name: "index_name",
 							Name: "index_name",
-							Type: TypeInfo{Type: TypeVarchar},
+							Type: NativeType{typ: TypeVarchar},
 							Kind: CLUSTERING_KEY,
 							Kind: CLUSTERING_KEY,
 						},
 						},
 						"value": &ColumnMetadata{
 						"value": &ColumnMetadata{
 							Name: "value",
 							Name: "value",
-							Type: TypeInfo{Type: TypeBlob},
+							Type: NativeType{typ: TypeBlob},
 							Kind: REGULAR,
 							Kind: REGULAR,
 						},
 						},
 					},
 					},
@@ -207,25 +207,25 @@ func TestCompileMetadata(t *testing.T) {
 					PartitionKey: []*ColumnMetadata{
 					PartitionKey: []*ColumnMetadata{
 						&ColumnMetadata{
 						&ColumnMetadata{
 							Name: "title",
 							Name: "title",
-							Type: TypeInfo{Type: TypeVarchar},
+							Type: NativeType{typ: TypeVarchar},
 						},
 						},
 					},
 					},
 					ClusteringColumns: []*ColumnMetadata{
 					ClusteringColumns: []*ColumnMetadata{
 						&ColumnMetadata{
 						&ColumnMetadata{
 							Name:  "revid",
 							Name:  "revid",
-							Type:  TypeInfo{Type: TypeTimeUUID},
+							Type:  NativeType{typ: TypeTimeUUID},
 							Order: ASC,
 							Order: ASC,
 						},
 						},
 					},
 					},
 					Columns: map[string]*ColumnMetadata{
 					Columns: map[string]*ColumnMetadata{
 						"title": &ColumnMetadata{
 						"title": &ColumnMetadata{
 							Name: "title",
 							Name: "title",
-							Type: TypeInfo{Type: TypeVarchar},
+							Type: NativeType{typ: TypeVarchar},
 							Kind: PARTITION_KEY,
 							Kind: PARTITION_KEY,
 						},
 						},
 						"revid": &ColumnMetadata{
 						"revid": &ColumnMetadata{
 							Name: "revid",
 							Name: "revid",
-							Type: TypeInfo{Type: TypeTimeUUID},
+							Type: NativeType{typ: TypeTimeUUID},
 							Kind: CLUSTERING_KEY,
 							Kind: CLUSTERING_KEY,
 						},
 						},
 					},
 					},
@@ -289,14 +289,14 @@ func TestCompileMetadata(t *testing.T) {
 					PartitionKey: []*ColumnMetadata{
 					PartitionKey: []*ColumnMetadata{
 						&ColumnMetadata{
 						&ColumnMetadata{
 							Name: "Key1",
 							Name: "Key1",
-							Type: TypeInfo{Type: TypeVarchar},
+							Type: NativeType{typ: TypeVarchar},
 						},
 						},
 					},
 					},
 					ClusteringColumns: []*ColumnMetadata{},
 					ClusteringColumns: []*ColumnMetadata{},
 					Columns: map[string]*ColumnMetadata{
 					Columns: map[string]*ColumnMetadata{
 						"Key1": &ColumnMetadata{
 						"Key1": &ColumnMetadata{
 							Name: "Key1",
 							Name: "Key1",
-							Type: TypeInfo{Type: TypeVarchar},
+							Type: NativeType{typ: TypeVarchar},
 							Kind: PARTITION_KEY,
 							Kind: PARTITION_KEY,
 						},
 						},
 					},
 					},
@@ -305,29 +305,29 @@ func TestCompileMetadata(t *testing.T) {
 					PartitionKey: []*ColumnMetadata{
 					PartitionKey: []*ColumnMetadata{
 						&ColumnMetadata{
 						&ColumnMetadata{
 							Name: "Column1",
 							Name: "Column1",
-							Type: TypeInfo{Type: TypeVarchar},
+							Type: NativeType{typ: TypeVarchar},
 						},
 						},
 					},
 					},
 					ClusteringColumns: []*ColumnMetadata{
 					ClusteringColumns: []*ColumnMetadata{
 						&ColumnMetadata{
 						&ColumnMetadata{
 							Name: "Column2",
 							Name: "Column2",
-							Type: TypeInfo{Type: TypeVarchar},
+							Type: NativeType{typ: TypeVarchar},
 						},
 						},
 					},
 					},
 					Columns: map[string]*ColumnMetadata{
 					Columns: map[string]*ColumnMetadata{
 						"Column1": &ColumnMetadata{
 						"Column1": &ColumnMetadata{
 							Name: "Column1",
 							Name: "Column1",
-							Type: TypeInfo{Type: TypeVarchar},
+							Type: NativeType{typ: TypeVarchar},
 							Kind: PARTITION_KEY,
 							Kind: PARTITION_KEY,
 						},
 						},
 						"Column2": &ColumnMetadata{
 						"Column2": &ColumnMetadata{
 							Name: "Column2",
 							Name: "Column2",
-							Type: TypeInfo{Type: TypeVarchar},
+							Type: NativeType{typ: TypeVarchar},
 							Kind: CLUSTERING_KEY,
 							Kind: CLUSTERING_KEY,
 						},
 						},
 						"Column3": &ColumnMetadata{
 						"Column3": &ColumnMetadata{
 							Name: "Column3",
 							Name: "Column3",
-							Type: TypeInfo{Type: TypeVarchar},
+							Type: NativeType{typ: TypeVarchar},
 							Kind: REGULAR,
 							Kind: REGULAR,
 						},
 						},
 					},
 					},
@@ -364,8 +364,8 @@ func assertKeyspaceMetadata(t *testing.T, actual, expected *KeyspaceMetadata) {
 					if keyT != at.PartitionKey[i].Table {
 					if keyT != at.PartitionKey[i].Table {
 						t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Table to be '%v' but was '%v'", expected.Name, keyT, i, keyT, at.PartitionKey[i].Table)
 						t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Table to be '%v' but was '%v'", expected.Name, keyT, i, keyT, at.PartitionKey[i].Table)
 					}
 					}
-					if et.PartitionKey[i].Type.Type != at.PartitionKey[i].Type.Type {
-						t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Type.Type to be %v but was %v", expected.Name, keyT, i, et.PartitionKey[i].Type.Type, at.PartitionKey[i].Type.Type)
+					if et.PartitionKey[i].Type.Type() != at.PartitionKey[i].Type.Type() {
+						t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Type.Type to be %v but was %v", expected.Name, keyT, i, et.PartitionKey[i].Type.Type(), at.PartitionKey[i].Type.Type())
 					}
 					}
 					if i != at.PartitionKey[i].ComponentIndex {
 					if i != at.PartitionKey[i].ComponentIndex {
 						t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].ComponentIndex to be %v but was %v", expected.Name, keyT, i, i, at.PartitionKey[i].ComponentIndex)
 						t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].ComponentIndex to be %v but was %v", expected.Name, keyT, i, i, at.PartitionKey[i].ComponentIndex)
@@ -388,8 +388,8 @@ func assertKeyspaceMetadata(t *testing.T, actual, expected *KeyspaceMetadata) {
 					if keyT != at.ClusteringColumns[i].Table {
 					if keyT != at.ClusteringColumns[i].Table {
 						t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Table to be '%v' but was '%v'", expected.Name, keyT, i, keyT, at.ClusteringColumns[i].Table)
 						t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Table to be '%v' but was '%v'", expected.Name, keyT, i, keyT, at.ClusteringColumns[i].Table)
 					}
 					}
-					if et.ClusteringColumns[i].Type.Type != at.ClusteringColumns[i].Type.Type {
-						t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Type.Type to be %v but was %v", expected.Name, keyT, i, et.ClusteringColumns[i].Type.Type, at.ClusteringColumns[i].Type.Type)
+					if et.ClusteringColumns[i].Type.Type() != at.ClusteringColumns[i].Type.Type() {
+						t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Type.Type to be %v but was %v", expected.Name, keyT, i, et.ClusteringColumns[i].Type.Type(), at.ClusteringColumns[i].Type.Type())
 					}
 					}
 					if i != at.ClusteringColumns[i].ComponentIndex {
 					if i != at.ClusteringColumns[i].ComponentIndex {
 						t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].ComponentIndex to be %v but was %v", expected.Name, keyT, i, i, at.ClusteringColumns[i].ComponentIndex)
 						t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].ComponentIndex to be %v but was %v", expected.Name, keyT, i, i, at.ClusteringColumns[i].ComponentIndex)
@@ -429,8 +429,8 @@ func assertKeyspaceMetadata(t *testing.T, actual, expected *KeyspaceMetadata) {
 						if keyT != ac.Table {
 						if keyT != ac.Table {
 							t.Errorf("Expected %s.Tables[%s].Columns[%s].Table to be '%v' but was '%v'", expected.Name, keyT, keyC, keyT, ac.Table)
 							t.Errorf("Expected %s.Tables[%s].Columns[%s].Table to be '%v' but was '%v'", expected.Name, keyT, keyC, keyT, ac.Table)
 						}
 						}
-						if ec.Type.Type != ac.Type.Type {
-							t.Errorf("Expected %s.Tables[%s].Columns[%s].Type.Type to be %v but was %v", expected.Name, keyT, keyC, ec.Type.Type, ac.Type.Type)
+						if ec.Type.Type() != ac.Type.Type() {
+							t.Errorf("Expected %s.Tables[%s].Columns[%s].Type.Type to be %v but was %v", expected.Name, keyT, keyC, ec.Type.Type(), ac.Type.Type())
 						}
 						}
 						if ec.Order != ac.Order {
 						if ec.Order != ac.Order {
 							t.Errorf("Expected %s.Tables[%s].Columns[%s].Order to be %v but was %v", expected.Name, keyT, keyC, ec.Order, ac.Order)
 							t.Errorf("Expected %s.Tables[%s].Columns[%s].Order to be %v but was %v", expected.Name, keyT, keyC, ec.Order, ac.Order)
@@ -633,38 +633,45 @@ func assertParseNonCompositeTypes(
 		}
 		}
 
 
 		// check the type
 		// check the type
-		if typeActual.Type != typeExpected.Type {
-			t.Errorf("%s: Expected to parse Type to %s but was %s", context, typeExpected.Type, typeActual.Type)
+		if typeActual.Type() != typeExpected.Type {
+			t.Errorf("%s: Expected to parse Type to %s but was %s", context, typeExpected.Type, typeActual.Type())
 		}
 		}
 		// check the custom
 		// check the custom
-		if typeActual.Custom != typeExpected.Custom {
-			t.Errorf("%s: Expected to parse Custom %s but was %s", context, typeExpected.Custom, typeActual.Custom)
+		if typeActual.Custom() != typeExpected.Custom {
+			t.Errorf("%s: Expected to parse Custom %s but was %s", context, typeExpected.Custom, typeActual.Custom())
 		}
 		}
+
+		collection, _ := typeActual.(CollectionType)
 		// check the elem
 		// check the elem
-		if typeActual.Elem == nil && typeExpected.Elem != nil {
-			t.Errorf("%s: Expected to parse Elem, but was nil ", context)
-		} else if typeExpected.Elem == nil && typeActual.Elem != nil {
-			t.Errorf("%s: Expected to not parse Elem, but was %+v", context, typeActual.Elem)
-		} else if typeActual.Elem != nil && typeExpected.Elem != nil {
-			assertParseNonCompositeTypes(
-				t,
-				context+".Elem",
-				[]assertTypeInfo{*typeExpected.Elem},
-				[]TypeInfo{*typeActual.Elem},
-			)
+		if typeExpected.Elem != nil {
+			if collection.Elem == nil {
+				t.Errorf("%s: Expected to parse Elem, but was nil ", context)
+			} else {
+				assertParseNonCompositeTypes(
+					t,
+					context+".Elem",
+					[]assertTypeInfo{*typeExpected.Elem},
+					[]TypeInfo{collection.Elem},
+				)
+			}
+		} else if collection.Elem != nil {
+			t.Errorf("%s: Expected to not parse Elem, but was %+v", context, collection.Elem)
 		}
 		}
+
 		// check the key
 		// check the key
-		if typeActual.Key == nil && typeExpected.Key != nil {
-			t.Errorf("%s: Expected to parse Key, but was nil ", context)
-		} else if typeExpected.Key == nil && typeActual.Key != nil {
-			t.Errorf("%s: Expected to not parse Key, but was %+v", context, typeActual.Key)
-		} else if typeActual.Key != nil && typeExpected.Key != nil {
-			assertParseNonCompositeTypes(
-				t,
-				context+".Key",
-				[]assertTypeInfo{*typeExpected.Key},
-				[]TypeInfo{*typeActual.Key},
-			)
+		if typeExpected.Key != nil {
+			if collection.Key == nil {
+				t.Errorf("%s: Expected to parse Key, but was nil ", context)
+			} else {
+				assertParseNonCompositeTypes(
+					t,
+					context+".Key",
+					[]assertTypeInfo{*typeExpected.Key},
+					[]TypeInfo{collection.Key},
+				)
+			}
+		} else if collection.Key != nil {
+			t.Errorf("%s: Expected to not parse Key, but was %+v", context, collection.Key)
 		}
 		}
 	}
 	}
 }
 }

+ 3 - 3
session.go

@@ -285,7 +285,7 @@ func (s *Session) routingKeyInfo(stmt string) (*routingKeyInfo, error) {
 	size := len(partitionKey)
 	size := len(partitionKey)
 	routingKeyInfo := &routingKeyInfo{
 	routingKeyInfo := &routingKeyInfo{
 		indexes: make([]int, size),
 		indexes: make([]int, size),
-		types:   make([]*TypeInfo, size),
+		types:   make([]TypeInfo, size),
 	}
 	}
 	for keyIndex, keyColumn := range partitionKey {
 	for keyIndex, keyColumn := range partitionKey {
 		// set an indicator for checking if the mapping is missing
 		// set an indicator for checking if the mapping is missing
@@ -759,7 +759,7 @@ type ColumnInfo struct {
 	Keyspace string
 	Keyspace string
 	Table    string
 	Table    string
 	Name     string
 	Name     string
-	TypeInfo *TypeInfo
+	TypeInfo TypeInfo
 }
 }
 
 
 func (c ColumnInfo) String() string {
 func (c ColumnInfo) String() string {
@@ -774,7 +774,7 @@ type routingKeyInfoLRU struct {
 
 
 type routingKeyInfo struct {
 type routingKeyInfo struct {
 	indexes []int
 	indexes []int
-	types   []*TypeInfo
+	types   []TypeInfo
 }
 }
 
 
 func (r *routingKeyInfoLRU) Remove(key string) {
 func (r *routingKeyInfoLRU) Remove(key string) {