Repository: apache/cassandra-gocql-driver

Issue:
Remove protoVersion2 support
Especially in frame.go, it is used in the data path and just 'pollutes' the code. Does anyone still need support for this ancient protocol? (and if they do, they can keep using older drivers).

Hints from issue comments:
@joao-r-reis: v2.0.0-rc1 with the fix for this has been released, it would be great if users could try it out and provide feedback on it before 2.0.0 GA

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 99f1def..7d0d5c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+### Removed
+
+- Drop support for old CQL protocol versions: 1 and 2 (CASSGO-75)
+
 ### Added
 
 - Support vector type [CASSGO-11](https://issues.apache.org/jira/browse/CASSGO-11)
diff --git a/conn.go b/conn.go
index 93f6cab..b7a3ed2 100644
--- a/conn.go
+++ b/conn.go
@@ -179,7 +179,7 @@ type Conn struct {
 	frameObserver  FrameHeaderObserver
 	streamObserver StreamObserver
 
-	headerBuf [maxFrameHeaderSize]byte
+	headerBuf [frameHeadSize]byte
 
 	streams *streams.IDGenerator
 	mu      sync.Mutex
@@ -784,12 +784,11 @@ func (c *Conn) recvSegment(ctx context.Context) error {
 		return err
 	}
 
-	const frameHeaderLength = 9
-	buf := bytes.NewBuffer(make([]byte, 0, head.length+frameHeaderLength))
+	buf := bytes.NewBuffer(make([]byte, 0, head.length+frameHeadSize))
 	buf.Write(frame)
 
 	// Computing how many bytes of message left to read
-	bytesToRead := head.length - len(frame) + frameHeaderLength
+	bytesToRead := head.length - len(frame) + frameHeadSize
 
 	err = c.recvPartialFrames(buf, bytesToRead)
 	if err != nil {
@@ -1738,10 +1737,6 @@ func (c *Conn) UseKeyspace(keyspace string) error {
 }
 
 func (c *Conn) executeBatch(ctx context.Context, batch *Batch) *Iter {
-	if c.version == protoVersion1 {
-		return &Iter{err: ErrUnsupported}
-	}
-
 	n := len(batch.Entries)
 	req := &writeBatchFrame{
 		typ:                   batch.Type,
diff --git a/frame.go b/frame.go
index c3b9606..f2f6c7c 100644
--- a/frame.go
+++ b/frame.go
@@ -307,7 +307,7 @@ var (
 	ErrFrameTooBig = errors.New("frame length is bigger than the maximum allowed")
 )
 
-const maxFrameHeaderSize = 9
+const frameHeadSize = 9
 
 func readInt(p []byte) int32 {
 	return int32(p[0])<<24 | int32(p[1])<<16 | int32(p[2])<<8 | int32(p[3])
@@ -364,9 +364,8 @@ type FrameHeaderObserver interface {
 type framer struct {
 	proto byte
 	// flags are for outgoing flags, enabling compression and tracing etc
-	flags    byte
-	compres  Compressor
-	headSize int
+	flags   byte
+	compres Compressor
 	// if this frame was read then the header will be here
 	header *frameHeader
 
@@ -398,15 +397,9 @@ func newFramer(compressor Compressor, version byte) *framer {
 
 	version &= protoVersionMask
 
-	headSize := 8
-	if version > protoVersion2 {
-		headSize = 9
-	}
-
 	f.compres = compressor
 	f.proto = version
 	f.flags = flags
-	f.headSize = headSize
 
 	f.header = nil
 	f.traceID = nil
@@ -426,43 +419,27 @@ func readHeader(r io.Reader, p []byte) (head frameHeader, err error) {
 
 	version := p[0] & protoVersionMask
 
-	if version < protoVersion1 || version > protoVersion5 {
+	if version < protoVersion3 || version > protoVersion5 {
 		return frameHeader{}, fmt.Errorf("gocql: unsupported protocol response version: %d", version)
 	}
 
-	headSize := 9
-	if version < protoVersion3 {
-		headSize = 8
-	}
-
-	_, err = io.ReadFull(r, p[1:headSize])
+	_, err = io.ReadFull(r, p[1:frameHeadSize])
 	if err != nil {
 		return frameHeader{}, err
 	}
 
-	p = p[:headSize]
+	p = p[:frameHeadSize]
 
 	head.version = protoVersion(p[0])
 	head.flags = p[1]
 
-	if version > protoVersion2 {
-		if len(p) != 9 {
-			return frameHeader{}, fmt.Errorf("not enough bytes to read header require 9 got: %d", len(p))
-		}
-
-		head.stream = int(int16(p[2])<<8 | int16(p[3]))
-		head.op = frameOp(p[4])
-		head.length = int(readInt(p[5:]))
-	} else {
-		if len(p) != 8 {
-			return frameHeader{}, fmt.Errorf("not enough bytes to read header require 8 got: %d", len(p))
-		}
-
-		head.stream = int(int8(p[2]))
-		head.op = frameOp(p[3])
-		head.length = int(readInt(p[4:]))
+	if len(p) != 9 {
+		return frameHeader{}, fmt.Errorf("not enough bytes to read header require 9 got: %d", len(p))
 	}
 
+	head.stream = int(int16(p[2])<<8 | int16(p[3]))
+	head.op = frameOp(p[4])
+	head.length = int(readInt(p[5:]))
 	return head, nil
 }
 
@@ -700,31 +677,18 @@ func (f *framer) readErrorMap() (errMap ErrorMap) {
 }
 
 func (f *framer) writeHeader(flags byte, op frameOp, stream int) {
-	if f.proto <= protoVersion2 {
-		f.buf = append(f.buf[:0],
-			f.proto, flags, byte(stream),
-			// pad out length
-			byte(op), 0, 0, 0, 0,
-		)
-	} else {
-		f.buf = append(f.buf[:0],
-			f.proto, flags, byte(stream>>8), byte(stream),
-			// pad out length
-			byte(op), 0, 0, 0, 0,
-		)
-	}
+	f.buf = append(f.buf[:0],
+		f.proto, flags, byte(stream>>8), byte(stream),
+		// pad out length
+		byte(op), 0, 0, 0, 0,
+	)
 }
 
 func (f *framer) setLength(length int) {
-	p := 4
-	if f.proto > protoVersion2 {
-		p = 5
-	}
-
-	f.buf[p+0] = byte(length >> 24)
-	f.buf[p+1] = byte(length >> 16)
-	f.buf[p+2] = byte(length >> 8)
-	f.buf[p+3] = byte(length)
+	f.buf[5] = byte(length >> 24)
+	f.buf[6] = byte(length >> 16)
+	f.buf[7] = byte(length >> 8)
+	f.buf[8] = byte(length)
 }
 
 func (f *framer) finish() error {
@@ -740,14 +704,14 @@ func (f *framer) finish() error {
 		}
 
 		// TODO: only compress frames which are big enough
-		compressed, err := f.compres.AppendCompressedWithLength(nil, f.buf[f.headSize:])
+		compressed, err := f.compres.AppendCompressedWithLength(nil, f.buf[frameHeadSize:])
 		if err != nil {
 			return err
 		}
 
-		f.buf = append(f.buf[:f.headSize], compressed...)
+		f.buf = append(f.buf[:frameHeadSize], compressed...)
 	}
-	length := len(f.buf) - f.headSize
+	length := len(f.buf) - frameHeadSize
 	f.setLength(length)
 
 	return nil
@@ -1180,11 +1144,6 @@ func (f *framer) parseResultPrepared() frame {
 	}
 
 	frame.reqMeta = f.parsePreparedMetadata()
-
-	if f.proto < protoVersion2 {
-		return frame
-	}
-
 	frame.respMeta = f.parseResultMetadata()
 
 	return frame
@@ -1240,87 +1199,65 @@ type schemaChangeAggregate struct {
 }
 
 func (f *framer) parseResultSchemaChange() frame {
-	if f.proto <= protoVersion2 {
-		change := f.readString()
-		keyspace := f.readString()
-		table := f.readString()
-
-		if table != "" {
-			return &schemaChangeTable{
-				frameHeader: *f.header,
-				change:      change,
-				keyspace:    keyspace,
-				object:      table,
-			}
-		} else {
-			return &schemaChangeKeyspace{
-				frameHeader: *f.header,
-				change:      change,
-				keyspace:    keyspace,
-			}
+	change := f.readString()
+	target := f.readString()
+
+	// TODO: could just use a separate type for each target
+	switch target {
+	case "KEYSPACE":
+		frame := &schemaChangeKeyspace{
+			frameHeader: *f.header,
+			change:      change,
 		}
-	} else {
-		change := f.readString()
-		target := f.readString()
-
-		// TODO: could just use a separate type for each target
-		switch target {
-		case "KEYSPACE":
-			frame := &schemaChangeKeyspace{
-				frameHeader: *f.header,
-				change:      change,
-			}
 
-			frame.keyspace = f.readString()
+		frame.keyspace = f.readString()
 
-			return frame
-		case "TABLE":
-			frame := &schemaChangeTable{
-				frameHeader: *f.header,
-				change:      change,
-			}
+		return frame
+	case "TABLE":
+		frame := &schemaChangeTable{
+			frameHeader: *f.header,
+			change:      change,
+		}
 
-			frame.keyspace = f.readString()
-			frame.object = f.readString()
+		frame.keyspace = f.readString()
+		frame.object = f.readString()
 
-			return frame
-		case "TYPE":
-			frame := &schemaChangeType{
-				frameHeader: *f.header,
-				change:      change,
-			}
+		return frame
+	case "TYPE":
+		frame := &schemaChangeType{
+			frameHeader: *f.header,
+			change:      change,
+		}
 
-			frame.keyspace = f.readString()
-			frame.object = f.readString()
+		frame.keyspace = f.readString()
+		frame.object = f.readString()
 
-			return frame
-		case "FUNCTION":
-			frame := &schemaChangeFunction{
-				frameHeader: *f.header,
-				change:      change,
-			}
+		return frame
+	case "FUNCTION":
+		frame := &schemaChangeFunction{
+			frameHeader: *f.header,
+			change:      change,
+		}
 
-			frame.keyspace = f.readString()
-			frame.name = f.readString()
-			frame.args = f.readStringList()
+		frame.keyspace = f.readString()
+		frame.name = f.readString()
+		frame.args = f.readStringList()
 
-			return frame
-		case "AGGREGATE":
-			frame := &schemaChangeAggregate{
-				frameHeader: *f.header,
-				change:      change,
-			}
+		return frame
+	case "AGGREGATE":
+		frame := &schemaChangeAggregate{
+			frameHeader: *f.header,
+			change:      change,
+		}
 
-			frame.keyspace = f.readString()
-			frame.name = f.readString()
-			frame.args = f.readStringList()
+		frame.keyspace = f.readString()
+		frame.name = f.readString()
+		frame.args = f.readStringList()
 
-			return frame
-		default:
-			panic(fmt.Errorf("gocql: unknown SCHEMA_CHANGE target: %q change: %q", target, change))
-		}
+		return frame
+	default:
+		panic(fmt.Errorf("gocql: unknown SCHEMA_CHANGE target: %q change: %q", target, change))
 	}
-
 }
 
 type authenticateFrame struct {
@@ -1474,10 +1411,6 @@ func (q queryParams) String() string {
 func (f *framer) writeQueryParams(opts *queryParams) {
 	f.writeConsistency(opts.consistency)
 
-	if f.proto == protoVersion1 {
-		return
-	}
-
 	var flags uint32
 	names := false
 
@@ -1497,16 +1430,13 @@ func (f *framer) writeQueryParams(opts *queryParams) {
 		flags |= flagWithSerialConsistency
 	}
 
-	// protoV3 specific things
-	if f.proto > protoVersion2 {
-		if opts.defaultTimestamp {
-			flags |= flagDefaultTimestamp
-		}
+	if opts.defaultTimestamp {
+		flags |= flagDefaultTimestamp
+	}
 
-		if len(opts.values) > 0 && opts.values[0].name != "" {
-			flags |= flagWithNameValues
-			names = true
-		}
+	if len(opts.values) > 0 && opts.values[0].name != "" {
+		flags |= flagWithNameValues
+		names = true
 	}
 
 	if opts.keyspace != "" {
@@ -1556,7 +1486,7 @@ func (f *framer) writeQueryParams(opts *queryParams) {
 		f.writeConsistency(opts.serialConsistency)
 	}
 
-	if f.proto > protoVersion2 && opts.defaultTimestamp {
+	if opts.defaultTimestamp {
 		// timestamp in microseconds
 		var ts int64
 		if opts.defaultTimestampValue != 0 {
@@ -1645,20 +1575,7 @@ func (f *framer) writeExecuteFrame(streamID int, preparedID, resultMetadataID []
 		f.writeShortBytes(resultMetadataID)
 	}
 
-	if f.proto > protoVersion1 {
-		f.writeQueryParams(params)
-	} else {
-		n := len(params.values)
-		f.writeShort(uint16(n))
-		for i := 0; i < n; i++ {
-			if params.values[i].isUnset {
-				f.writeUnset()
-			} else {
-				f.writeBytes(params.values[i].value)
-			}
-		}
-		f.writeConsistency(params.consistency)
-	}
+	f.writeQueryParams(params)
 
 	return f.finish()
 }
@@ -1719,7 +1636,7 @@ func (f *framer) writeBatchFrame(streamID int, w *writeBatchFrame, customPayload
 		f.writeShort(uint16(len(b.values)))
 		for j := range b.values {
 			col := b.values[j]
-			if f.proto > protoVersion2 && col.name != "" {
+			if col.name != "" {
 				// TODO: move this check into the caller and set a flag on writeBatchFrame
 				// to indicate using named values
 				if f.proto <= protoVersion5 {
@@ -1738,13 +1655,12 @@ func (f *framer) writeBatchFrame(streamID int, w *writeBatchFrame, customPayload
 
 	f.writeConsistency(w.consistency)
 
-	if f.proto > protoVersion2 {
-		if w.serialConsistency > 0 {
-			flags |= flagWithSerialConsistency
-		}
-		if w.defaultTimestamp {
-			flags |= flagDefaultTimestamp
-		}
+	if w.serialConsistency > 0 {
+		flags |= flagWithSerialConsistency
+	}
+
+	if w.defaultTimestamp {
+		flags |= flagDefaultTimestamp
 	}
 
 	if w.keyspace != "" {
diff --git a/marshal.go b/marshal.go
index d0cf673..65ab656 100644
--- a/marshal.go
+++ b/marshal.go
@@ -116,7 +116,6 @@ func Marshal(info TypeInfo, value interface{}) ([]byte, error) {
 	if info.Version() < protoVersion1 {
 		panic("protocol version not set")
 	}
-
 	if valueRef := reflect.ValueOf(value); valueRef.Kind() == reflect.Ptr {
 		if valueRef.IsNil() {
 			return nil, nil
@@ -1565,25 +1564,15 @@ func encVint(v int64) []byte {
 	return buf
 }
 
-func writeCollectionSize(info CollectionType, n int, buf *bytes.Buffer) error {
-	if info.proto > protoVersion2 {
-		if n > math.MaxInt32 {
-			return marshalErrorf("marshal: collection too large")
-		}
-
-		buf.WriteByte(byte(n >> 24))
-		buf.WriteByte(byte(n >> 16))
-		buf.WriteByte(byte(n >> 8))
-		buf.WriteByte(byte(n))
-	} else {
-		if n > math.MaxUint16 {
-			return marshalErrorf("marshal: collection too large")
-		}
-
-		buf.WriteByte(byte(n >> 8))
-		buf.WriteByte(byte(n))
+func writeCollectionSize(n int, buf *bytes.Buffer) error {
+	if n > math.MaxInt32 {
+		return marshalErrorf("marshal: collection too large")
 	}
 
+	buf.WriteByte(byte(n >> 24))
+	buf.WriteByte(byte(n >> 16))
+	buf.WriteByte(byte(n >> 8))
+	buf.WriteByte(byte(n))
 	return nil
 }
 
@@ -1611,7 +1600,7 @@ func marshalList(info TypeInfo, value interface{}) ([]byte, error) {
 		buf := &bytes.Buffer{}
 		n := rv.Len()
 
-		if err := writeCollectionSize(listInfo, n, buf); err != nil {
+		if err := writeCollectionSize(n, buf); err != nil {
 			return nil, err
 		}
 
@@ -1622,10 +1611,10 @@ func marshalList(info TypeInfo, value interface{}) ([]byte, error) {
 			}
 			itemLen := len(item)
 			// Set the value to null for supported protocols
-			if item == nil && listInfo.proto > protoVersion2 {
+			if item == nil {
 				itemLen = -1
 			}
-			if err := writeCollectionSize(listInfo, itemLen, buf); err != nil {
+			if err := writeCollectionSize(itemLen, buf); err != nil {
 				return nil, err
 			}
 			buf.Write(item)
@@ -1645,20 +1634,12 @@ func marshalList(info TypeInfo, value interface{}) ([]byte, error) {
 	return nil, marshalErrorf("can not marshal %T into %s. Accepted types: slice, array, map[]struct.", value, info)
 }
 
-func readCollectionSize(info CollectionType, data []byte) (size, read int, err error) {
-	if info.proto > protoVersion2 {
-		if len(data) < 4 {
-			return 0, 0, unmarshalErrorf("unmarshal list: unexpected eof")
-		}
-		size = int(int32(data[0])<<24 | int32(data[1])<<16 | int32(data[2])<<8 | int32(data[3]))
-		read = 4
-	} else {
-		if len(data) < 2 {
-			return 0, 0, unmarshalErrorf("unmarshal list: unexpected eof")
-		}
-		size = int(data[0])<<8 | int(data[1])
-		read = 2
+func readCollectionSize(data []byte) (size, read int, err error) {
+	if len(data) < 4 {
+		return 0, 0, unmarshalErrorf("unmarshal list: unexpected eof")
 	}
+	size = int(int32(data[0])<<24 | int32(data[1])<<16 | int32(data[2])<<8 | int32(data[3]))
+	read = 4
 	return
 }
 
@@ -1688,7 +1669,7 @@ func unmarshalList(info TypeInfo, data []byte, value interface{}) error {
 			rv.Set(reflect.Zero(t))
 			return nil
 		}
-		n, p, err := readCollectionSize(listInfo, data)
+		n, p, err := readCollectionSize(data)
 		if err != nil {
 			return err
 		}
@@ -1701,7 +1682,7 @@ func unmarshalList(info TypeInfo, data []byte, value interface{}) error {
 			rv.Set(reflect.MakeSlice(t, n, n))
 		}
 		for i := 0; i < n; i++ {
-			m, p, err := readCollectionSize(listInfo, data)
+			m, p, err := readCollectionSize(data)
 			if err != nil {
 				return err
 			}
@@ -1909,7 +1890,7 @@ 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(n, buf); err != nil {
 		return nil, err
 	}
 
@@ -1921,10 +1902,10 @@ func marshalMap(info TypeInfo, value interface{}) ([]byte, error) {
 		}
 		itemLen := len(item)
 		// Set the key to null for supported protocols
-		if item == nil && mapInfo.proto > protoVersion2 {
+		if item == nil {
 			itemLen = -1
 		}
-		if err := writeCollectionSize(mapInfo, itemLen, buf); err != nil {
+		if err := writeCollectionSize(itemLen, buf); err != nil {
 			return nil, err
 		}
 		buf.Write(item)
@@ -1935,10 +1916,10 @@ func marshalMap(info TypeInfo, value interface{}) ([]byte, error) {
 		}
 		itemLen = len(item)
 		// Set the value to null for supported protocols
-		if item == nil && mapInfo.proto > protoVersion2 {
+		if item == nil {
 			itemLen = -1
 		}
-		if err := writeCollectionSize(mapInfo, itemLen, buf); err != nil {
+		if err := writeCollectionSize(itemLen, buf); err != nil {
 			return nil, err
 		}
 		buf.Write(item)
@@ -1965,7 +1946,7 @@ func unmarshalMap(info TypeInfo, data []byte, value interface{}) error {
 		rv.Set(reflect.Zero(t))
 		return nil
 	}
-	n, p, err := readCollectionSize(mapInfo, data)
+	n, p, err := readCollectionSize(data)
 	if err != nil {
 		return err
 	}
@@ -1975,7 +1956,7 @@ func unmarshalMap(info TypeInfo, data []byte, value interface{}) error {
 	rv.Set(reflect.MakeMapWithSize(t, n))
 	data = data[p:]
 	for i := 0; i < n; i++ {
-		m, p, err := readCollectionSize(mapInfo, data)
+		m, p, err := readCollectionSize(data)
 		if err != nil {
 			return err
 		}
@@ -1994,7 +1975,7 @@ func unmarshalMap(info TypeInfo, data []byte, value interface{}) error {
 			return err
 		}
 
-		m, p, err = readCollectionSize(mapInfo, data)
+		m, p, err = readCollectionSize(data)
 		if err != nil {
 			return err
 		}
diff --git a/metadata.go b/metadata.go
index c7f8e4b..c3e6ad3 100644
--- a/metadata.go
+++ b/metadata.go
@@ -32,7 +32,6 @@ import (
 	"encoding/hex"
 	"encoding/json"
 	"fmt"
-	"strconv"
 	"strings"
 	"sync"
 )
@@ -388,122 +387,7 @@ func compileMetadata(
 		table.OrderedColumns = append(table.OrderedColumns, col.Name)
 	}
 
-	if protoVersion == protoVersion1 {
-		compileV1Metadata(tables, protoVersion, logger)
-	} else {
-		compileV2Metadata(tables, protoVersion, logger)
-	}
-}
-
-// Compiles derived information from TableMetadata which have had
-// ColumnMetadata added already. V1 protocol does not return as much
-// column metadata as V2+ (because V1 doesn't support the "type" column in the
-// system.schema_columns table) so determining PartitionKey and ClusterColumns
-// is more complex.
-func compileV1Metadata(tables []TableMetadata, protoVer int, logger StdLogger) {
-	for i := range tables {
-		table := &tables[i]
-
-		// decode the key validator
-		keyValidatorParsed := parseType(table.KeyValidator, byte(protoVer), logger)
-		// decode the comparator
-		comparatorParsed := parseType(table.Comparator, byte(protoVer), logger)
-
-		// the partition key length is the same as the number of types in the
-		// key validator
-		table.PartitionKey = make([]*ColumnMetadata, len(keyValidatorParsed.types))
-
-		// V1 protocol only returns "regular" columns from
-		// system.schema_columns (there is no type field for columns)
-		// so the alias information is used to
-		// create the partition key and clustering columns
-
-		// construct the partition key from the alias
-		for i := range table.PartitionKey {
-			var alias string
-			if len(table.KeyAliases) > i {
-				alias = table.KeyAliases[i]
-			} else if i == 0 {
-				alias = DEFAULT_KEY_ALIAS
-			} else {
-				alias = DEFAULT_KEY_ALIAS + strconv.Itoa(i+1)
-			}
-
-			column := &ColumnMetadata{
-				Keyspace:       table.Keyspace,
-				Table:          table.Name,
-				Name:           alias,
-				Type:           keyValidatorParsed.types[i],
-				Kind:           ColumnPartitionKey,
-				ComponentIndex: i,
-			}
-
-			table.PartitionKey[i] = column
-			table.Columns[alias] = column
-		}
-
-		// determine the number of clustering columns
-		size := len(comparatorParsed.types)
-		if comparatorParsed.isComposite {
-			if len(comparatorParsed.collections) != 0 ||
-				(len(table.ColumnAliases) == size-1 &&
-					comparatorParsed.types[size-1].Type() == TypeVarchar) {
-				size = size - 1
-			}
-		} else {
-			if !(len(table.ColumnAliases) != 0 || len(table.Columns) == 0) {
-				size = 0
-			}
-		}
-
-		table.ClusteringColumns = make([]*ColumnMetadata, size)
-
-		for i := range table.ClusteringColumns {
-			var alias string
-			if len(table.ColumnAliases) > i {
-				alias = table.ColumnAliases[i]
-			} else if i == 0 {
-				alias = DEFAULT_COLUMN_ALIAS
-			} else {
-				alias = DEFAULT_COLUMN_ALIAS + strconv.Itoa(i+1)
-			}
-
-			order := ASC
-			if comparatorParsed.reversed[i] {
-				order = DESC
-			}
-
-			column := &ColumnMetadata{
-				Keyspace:       table.Keyspace,
-				Table:          table.Name,
-				Name:           alias,
-				Type:           comparatorParsed.types[i],
-				Order:          order,
-				Kind:           ColumnClusteringKey,
-				ComponentIndex: i,
-			}
-
-			table.ClusteringColumns[i] = column
-			table.Columns[alias] = column
-		}
-
-		if size != len(comparatorParsed.types)-1 {
-			alias := DEFAULT_VALUE_ALIAS
-			if len(table.ValueAlias) > 0 {
-				alias = table.ValueAlias
-			}
-			// decode the default validator
-			defaultValidatorParsed := parseType(table.DefaultValidator, byte(protoVer), logger)
-			column := &ColumnMetadata{
-				Keyspace: table.Keyspace,
-				Table:    table.Name,
-				Name:     alias,
-				Type:     defaultValidatorParsed.types[0],
-				Kind:     ColumnRegular,
-			}
-			table.Columns[alias] = column
-		}
-	}
+	compileV2Metadata(tables, protoVersion, logger)
 }
 
 // The simpler compile case for V2+ protocol
@@ -611,9 +495,6 @@ func getTableMetadata(session *Session, keyspaceName string) ([]TableMetadata, e
 		iter *Iter
 		scan func(iter *Iter, table *TableMetadata) bool
 		stmt string
-
-		keyAliasesJSON    []byte
-		columnAliasesJSON []byte
 	)
 
 	if session.useSystemSchema { // Cassandra 3.x+
@@ -647,31 +528,6 @@ func getTableMetadata(session *Session, keyspaceName string) ([]TableMetadata, e
 			}
 			return r
 		}
-	} else if session.cfg.ProtoVersion == protoVersion1 {
-		// we have key aliases
-		stmt = `
-		SELECT
-			columnfamily_name,
-			key_validator,
-			comparator,
-			default_validator,
-			key_aliases,
-			column_aliases,
-			value_alias
-		FROM system.schema_columnfamilies
-		WHERE keyspace_name = ?`
-
-		scan = func(iter *Iter, table *TableMetadata) bool {
-			return iter.Scan(
-				&table.Name,
-				&table.KeyValidator,
-				&table.Comparator,
-				&table.DefaultValidator,
-				&keyAliasesJSON,
-				&columnAliasesJSON,
-				&table.ValueAlias,
-			)
-		}
 	} else {
 		stmt = `
 		SELECT
@@ -698,34 +554,6 @@ func getTableMetadata(session *Session, keyspaceName string) ([]TableMetadata, e
 	table := TableMetadata{Keyspace: keyspaceName}
 
 	for scan(iter, &table) {
-		var err error
-
-		// decode the key aliases
-		if keyAliasesJSON != nil {
-			table.KeyAliases = []string{}
-			err = json.Unmarshal(keyAliasesJSON, &table.KeyAliases)
-			if err != nil {
-				iter.Close()
-				return nil, fmt.Errorf(
-					"invalid JSON value '%s' as key_aliases for in table '%s': %v",
-					keyAliasesJSON, table.Name, err,
-				)
-			}
-		}
-
-		// decode the column aliases
-		if columnAliasesJSON != nil {
-			table.ColumnAliases = []string{}
-			err = json.Unmarshal(columnAliasesJSON, &table.ColumnAliases)
-			if err != nil {
-				iter.Close()
-				return nil, fmt.Errorf(
-					"invalid JSON value '%s' as column_aliases for in table '%s': %v",
-					columnAliasesJSON, table.Name, err,
-				)
-			}
-		}
-
 		tables = append(tables, table)
 		table = TableMetadata{Keyspace: keyspaceName}
 	}
@@ -933,9 +761,6 @@ func getTypeInfo(t string, protoVer byte, logger StdLogger) TypeInfo {
 }
 
 func getUserTypeMetadata(session *Session, keyspaceName string) ([]UserTypeMetadata, error) {
-	if session.cfg.ProtoVersion == protoVersion1 {
-		return nil, nil
-	}
 	var tableName string
 	if session.useSystemSchema {
 		tableName = "system_schema.types"
@@ -1179,7 +1004,7 @@ func getMaterializedViewsMetadata(session *Session, keyspaceName string) ([]Mate
 }
 
 func getFunctionsMetadata(session *Session, keyspaceName string) ([]FunctionMetadata, error) {
-	if session.cfg.ProtoVersion == protoVersion1 || !session.hasAggregatesAndFunctions {
+	if !session.hasAggregatesAndFunctions {
 		return nil, nil
 	}
 	var tableName string
@@ -1234,7 +1059,7 @@ func getFunctionsMetadata(session *Session, keyspaceName string) ([]FunctionMeta
 }
 
 func getAggregatesMetadata(session *Session, keyspaceName string) ([]AggregateMetadata, error) {
-	if session.cfg.ProtoVersion == protoVersion1 || !session.hasAggregatesAndFunctions {
+	if !session.hasAggregatesAndFunctions {
 		return nil, nil
 	}
 	var tableName string



Test patch:
diff --git a/batch_test.go b/batch_test.go
index 3a8a7e7..47adff8 100644
--- a/batch_test.go
+++ b/batch_test.go
@@ -40,10 +40,6 @@ func TestBatch_Errors(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
 
-	if session.cfg.ProtoVersion < protoVersion2 {
-		t.Skip("atomic batches not supported. Please use Cassandra >= 2.0")
-	}
-
 	if err := createTable(session, `CREATE TABLE gocql_test.batch_errors (id int primary key, val inet)`); err != nil {
 		t.Fatal(err)
 	}
@@ -59,10 +55,6 @@ func TestBatch_WithTimestamp(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
 
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("Batch timestamps are only available on protocol >= 3")
-	}
-
 	if err := createTable(session, `CREATE TABLE gocql_test.batch_ts (id int primary key, val text)`); err != nil {
 		t.Fatal(err)
 	}
diff --git a/cassandra_test.go b/cassandra_test.go
index 039fcc1..1a9b69f 100644
--- a/cassandra_test.go
+++ b/cassandra_test.go
@@ -2284,25 +2284,7 @@ func TestGetTableMetadata(t *testing.T) {
 	if testTable == nil {
 		t.Fatal("Expected table metadata for name 'test_table_metadata'")
 	}
-	if session.cfg.ProtoVersion == protoVersion1 {
-		if testTable.KeyValidator != "org.apache.cassandra.db.marshal.Int32Type" {
-			t.Errorf("Expected test_table_metadata key validator to be 'org.apache.cassandra.db.marshal.Int32Type' but was '%s'", testTable.KeyValidator)
-		}
-		if testTable.Comparator != "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.Int32Type,org.apache.cassandra.db.marshal.UTF8Type)" {
-			t.Errorf("Expected test_table_metadata key validator to be 'org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.Int32Type,org.apache.cassandra.db.marshal.UTF8Type)' but was '%s'", testTable.Comparator)
-		}
-		if testTable.DefaultValidator != "org.apache.cassandra.db.marshal.BytesType" {
-			t.Errorf("Expected test_table_metadata key validator to be 'org.apache.cassandra.db.marshal.BytesType' but was '%s'", testTable.DefaultValidator)
-		}
-		expectedKeyAliases := []string{"first_id"}
-		if !reflect.DeepEqual(testTable.KeyAliases, expectedKeyAliases) {
-			t.Errorf("Expected key aliases %v but was %v", expectedKeyAliases, testTable.KeyAliases)
-		}
-		expectedColumnAliases := []string{"second_id"}
-		if !reflect.DeepEqual(testTable.ColumnAliases, expectedColumnAliases) {
-			t.Errorf("Expected key aliases %v but was %v", expectedColumnAliases, testTable.ColumnAliases)
-		}
-	}
+
 	if testTable.ValueAlias != "" {
 		t.Errorf("Expected value alias '' but was '%s'", testTable.ValueAlias)
 	}
@@ -3228,10 +3210,6 @@ func TestUnmarshallNestedTypes(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
 
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("can not have frozen types in cassandra < 2.1.3")
-	}
-
 	if err := createTable(session, `CREATE TABLE gocql_test.test_557 (
 		    id text PRIMARY KEY,
 		    val list<frozen<map<text, text> > >
diff --git a/conn_test.go b/conn_test.go
index f78361d..cb76d90 100644
--- a/conn_test.go
+++ b/conn_test.go
@@ -53,7 +53,7 @@ import (
 )
 
 const (
-	defaultProto = protoVersion2
+	defaultProto = protoVersion4
 )
 
 func TestApprove(t *testing.T) {
@@ -1056,18 +1056,13 @@ func (nts newTestServerOpts) newServer(t testing.TB, ctx context.Context) *TestS
 		t.Fatal(err)
 	}
 
-	headerSize := 8
-	if nts.protocol > protoVersion2 {
-		headerSize = 9
-	}
-
 	ctx, cancel := context.WithCancel(ctx)
 	srv := &TestServer{
 		Address:    listen.Addr().String(),
 		listen:     listen,
 		t:          t,
 		protocol:   nts.protocol,
-		headerSize: headerSize,
+		headerSize: 9,
 		ctx:        ctx,
 		cancel:     cancel,
 
@@ -1103,18 +1098,13 @@ func NewSSLTestServer(t testing.TB, protocol uint8, ctx context.Context) *TestSe
 		t.Fatal(err)
 	}
 
-	headerSize := 8
-	if protocol > protoVersion2 {
-		headerSize = 9
-	}
-
 	ctx, cancel := context.WithCancel(ctx)
 	srv := &TestServer{
 		Address:    listen.Addr().String(),
 		listen:     listen,
 		t:          t,
 		protocol:   protocol,
-		headerSize: headerSize,
+		headerSize: 9,
 		ctx:        ctx,
 		cancel:     cancel,
 	}
diff --git a/frame_test.go b/frame_test.go
index 8cb9024..6bf4922 100644
--- a/frame_test.go
+++ b/frame_test.go
@@ -108,14 +108,14 @@ func TestFrameReadTooLong(t *testing.T) {
 	r := &bytes.Buffer{}
 	r.Write(make([]byte, maxFrameSize+1))
 	// write a new header right after this frame to verify that we can read it
-	r.Write([]byte{0x02, 0x00, 0x00, byte(opReady), 0x00, 0x00, 0x00, 0x00})
+	r.Write([]byte{protoVersionMask & protoVersion3, 0x00, 0x00, 0x00, byte(opReady), 0x00, 0x00, 0x00, 0x00})
 
-	framer := newFramer(nil, 2)
+	framer := newFramer(nil, 3)
 
 	head := frameHeader{
-		version: 2,
+		version: protoVersion3,
 		op:      opReady,
-		length:  r.Len() - 8,
+		length:  r.Len() - frameHeadSize,
 	}
 
 	err := framer.readFrame(r, &head)
@@ -123,7 +123,7 @@ func TestFrameReadTooLong(t *testing.T) {
 		t.Fatalf("expected to get %v got %v", ErrFrameTooBig, err)
 	}
 
-	head, err = readHeader(r, make([]byte, 8))
+	head, err = readHeader(r, make([]byte, frameHeadSize))
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/marshal_test.go b/marshal_test.go
index 969d8e5..cdbc646 100644
--- a/marshal_test.go
+++ b/marshal_test.go
@@ -59,56 +59,56 @@ var marshalTests = []struct {
 	UnmarshalError error
 }{
 	{
-		NativeType{proto: 2, typ: TypeVarchar},
+		NativeType{proto: protoVersion3, typ: TypeVarchar},
 		[]byte("hello world"),
 		[]byte("hello world"),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarchar},
+		NativeType{proto: protoVersion3, typ: TypeVarchar},
 		[]byte("hello world"),
 		"hello world",
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarchar},
+		NativeType{proto: protoVersion3, typ: TypeVarchar},
 		[]byte(nil),
 		[]byte(nil),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarchar},
+		NativeType{proto: protoVersion3, typ: TypeVarchar},
 		[]byte("hello world"),
 		MyString("hello world"),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarchar},
+		NativeType{proto: protoVersion3, typ: TypeVarchar},
 		[]byte("HELLO WORLD"),
 		CustomString("hello world"),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBlob},
+		NativeType{proto: protoVersion3, typ: TypeBlob},
 		[]byte("hello\x00"),
 		[]byte("hello\x00"),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBlob},
+		NativeType{proto: protoVersion3, typ: TypeBlob},
 		[]byte(nil),
 		[]byte(nil),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTimeUUID},
+		NativeType{proto: protoVersion3, typ: TypeTimeUUID},
 		[]byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0},
 		func() UUID {
 			x, _ := UUIDFromBytes([]byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0})
@@ -118,287 +118,287 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTimeUUID},
+		NativeType{proto: protoVersion3, typ: TypeTimeUUID},
 		[]byte{0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0},
 		[]byte{0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0},
 		MarshalError("can not marshal []byte 6 bytes long into timeuuid, must be exactly 16 bytes long"),
 		UnmarshalError("unable to parse UUID: UUIDs must be exactly 16 bytes long"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeTimeUUID},
+		NativeType{proto: protoVersion3, typ: TypeTimeUUID},
 		[]byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0},
 		[16]byte{0x3d, 0xcd, 0x98, 0x0, 0xf3, 0xd9, 0x11, 0xbf, 0x86, 0xd4, 0xb8, 0xe8, 0x56, 0x2c, 0xc, 0xd0},
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x00\x00\x00\x00"),
 		0,
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x01\x02\x03\x04"),
 		int(16909060),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x01\x02\x03\x04"),
 		AliasInt(16909060),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x80\x00\x00\x00"),
 		int32(math.MinInt32),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x7f\xff\xff\xff"),
 		int32(math.MaxInt32),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x00\x00\x00\x00"),
 		"0",
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x01\x02\x03\x04"),
 		"16909060",
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x80\x00\x00\x00"),
 		"-2147483648", // math.MinInt32
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x7f\xff\xff\xff"),
 		"2147483647", // math.MaxInt32
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\x00\x00\x00\x00"),
 		0,
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x01\x02\x03\x04\x05\x06\x07\x08"),
 		72623859790382856,
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x80\x00\x00\x00\x00\x00\x00\x00"),
 		int64(math.MinInt64),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x7f\xff\xff\xff\xff\xff\xff\xff"),
 		int64(math.MaxInt64),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\x00\x00\x00\x00"),
 		"0",
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x01\x02\x03\x04\x05\x06\x07\x08"),
 		"72623859790382856",
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x80\x00\x00\x00\x00\x00\x00\x00"),
 		"-9223372036854775808", // math.MinInt64
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x7f\xff\xff\xff\xff\xff\xff\xff"),
 		"9223372036854775807", // math.MaxInt64
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBoolean},
+		NativeType{proto: protoVersion3, typ: TypeBoolean},
 		[]byte("\x00"),
 		false,
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBoolean},
+		NativeType{proto: protoVersion3, typ: TypeBoolean},
 		[]byte("\x01"),
 		true,
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeFloat},
+		NativeType{proto: protoVersion3, typ: TypeFloat},
 		[]byte("\x40\x49\x0f\xdb"),
 		float32(3.14159265),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDouble},
+		NativeType{proto: protoVersion3, typ: TypeDouble},
 		[]byte("\x40\x09\x21\xfb\x53\xc8\xd4\xf1"),
 		float64(3.14159265),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDecimal},
+		NativeType{proto: protoVersion3, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x00\x00"),
 		inf.NewDec(0, 0),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDecimal},
+		NativeType{proto: protoVersion3, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x00\x64"),
 		inf.NewDec(100, 0),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDecimal},
+		NativeType{proto: protoVersion3, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x02\x19"),
 		decimalize("0.25"),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDecimal},
+		NativeType{proto: protoVersion3, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x13\xD5\a;\x20\x14\xA2\x91"),
 		decimalize("-0.0012095473475870063"), // From the iconara/cql-rb test suite
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDecimal},
+		NativeType{proto: protoVersion3, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x13*\xF8\xC4\xDF\xEB]o"),
 		decimalize("0.0012095473475870063"), // From the iconara/cql-rb test suite
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDecimal},
+		NativeType{proto: protoVersion3, typ: TypeDecimal},
 		[]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
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDecimal},
+		NativeType{proto: protoVersion3, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\r\nJ\x04\"^\x91\x04\x8a\xb1\x18\xfe"),
 		decimalize("1243878957943.1234124191998"), // From the datastax/python-driver test suite
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDecimal},
+		NativeType{proto: protoVersion3, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x06\xe5\xde]\x98Y"),
 		decimalize("-112233.441191"), // From the datastax/python-driver test suite
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDecimal},
+		NativeType{proto: protoVersion3, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x14\x00\xfa\xce"),
 		decimalize("0.00000000000000064206"), // From the datastax/python-driver test suite
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDecimal},
+		NativeType{proto: protoVersion3, typ: TypeDecimal},
 		[]byte("\x00\x00\x00\x14\xff\x052"),
 		decimalize("-0.00000000000000064206"), // From the datastax/python-driver test suite
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDecimal},
+		NativeType{proto: protoVersion3, typ: TypeDecimal},
 		[]byte("\xff\xff\xff\x9c\x00\xfa\xce"),
 		inf.NewDec(64206, -100), // From the datastax/python-driver test suite
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 4, typ: TypeTime},
+		NativeType{proto: protoVersion4, typ: TypeTime},
 		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 		time.Duration(int64(1376387523000)),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 4, typ: TypeTime},
+		NativeType{proto: protoVersion4, typ: TypeTime},
 		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 		int64(1376387523000),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTimestamp},
+		NativeType{proto: protoVersion3, typ: TypeTimestamp},
 		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 		time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTimestamp},
+		NativeType{proto: protoVersion3, typ: TypeTimestamp},
 		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 		int64(1376387523000),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 5, typ: TypeDuration},
+		NativeType{proto: protoVersion5, typ: TypeDuration},
 		[]byte("\x89\xa2\xc3\xc2\x9a\xe0F\x91\x06"),
 		Duration{Months: 1233, Days: 123213, Nanoseconds: 2312323},
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 5, typ: TypeDuration},
+		NativeType{proto: protoVersion5, typ: TypeDuration},
 		[]byte("\x89\xa1\xc3\xc2\x99\xe0F\x91\x05"),
 		Duration{Months: -1233, Days: -123213, Nanoseconds: -2312323},
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 5, typ: TypeDuration},
+		NativeType{proto: protoVersion5, typ: TypeDuration},
 		[]byte("\x02\x04\x80\xe6"),
 		Duration{Months: 1, Days: 2, Nanoseconds: 115},
 		nil,
@@ -406,73 +406,73 @@ var marshalTests = []struct {
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeList},
-			Elem:       NativeType{proto: 2, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeList},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
-		[]byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"),
+		[]byte("\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02"),
 		[]int{1, 2},
 		nil,
 		nil,
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeList},
-			Elem:       NativeType{proto: 2, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeList},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
-		[]byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"),
+		[]byte("\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02"),
 		[2]int{1, 2},
 		nil,
 		nil,
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeSet},
-			Elem:       NativeType{proto: 2, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeSet},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
-		[]byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"),
+		[]byte("\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02"),
 		[]int{1, 2},
 		nil,
 		nil,
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeSet},
-			Elem:       NativeType{proto: 2, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeSet},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
-		[]byte{0, 0}, // encoding of a list should always include the size of the collection
+		[]byte{0, 0, 0, 0}, // encoding of a list should always include the size of the collection
 		[]int{},
 		nil,
 		nil,
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeMap},
-			Key:        NativeType{proto: 2, typ: TypeVarchar},
-			Elem:       NativeType{proto: 2, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeMap},
+			Key:        NativeType{proto: protoVersion3, typ: TypeVarchar},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
-		[]byte("\x00\x01\x00\x03foo\x00\x04\x00\x00\x00\x01"),
+		[]byte("\x00\x00\x00\x01\x00\x00\x00\x03foo\x00\x00\x00\x04\x00\x00\x00\x01"),
 		map[string]int{"foo": 1},
 		nil,
 		nil,
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeMap},
-			Key:        NativeType{proto: 2, typ: TypeVarchar},
-			Elem:       NativeType{proto: 2, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeMap},
+			Key:        NativeType{proto: protoVersion3, typ: TypeVarchar},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
-		[]byte{0, 0},
+		[]byte{0, 0, 0, 0},
 		map[string]int{},
 		nil,
 		nil,
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeList},
-			Elem:       NativeType{proto: 2, typ: TypeVarchar},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeList},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeVarchar},
 		},
 		bytes.Join([][]byte{
-			[]byte("\x00\x01\xFF\xFF"),
+			[]byte("\x00\x00\x00\x01\x00\x00\xff\xff"),
 			bytes.Repeat([]byte("X"), math.MaxUint16)}, []byte("")),
 		[]string{strings.Repeat("X", math.MaxUint16)},
 		nil,
@@ -480,14 +480,14 @@ var marshalTests = []struct {
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeMap},
-			Key:        NativeType{proto: 2, typ: TypeVarchar},
-			Elem:       NativeType{proto: 2, typ: TypeVarchar},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeMap},
+			Key:        NativeType{proto: protoVersion3, typ: TypeVarchar},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeVarchar},
 		},
 		bytes.Join([][]byte{
-			[]byte("\x00\x01\xFF\xFF"),
+			[]byte("\x00\x00\x00\x01\x00\x00\xff\xff"),
 			bytes.Repeat([]byte("X"), math.MaxUint16),
-			[]byte("\xFF\xFF"),
+			[]byte("\x00\x00\xFF\xFF"),
 			bytes.Repeat([]byte("Y"), math.MaxUint16)}, []byte("")),
 		map[string]string{
 			strings.Repeat("X", math.MaxUint16): strings.Repeat("Y", math.MaxUint16),
@@ -496,119 +496,119 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarint},
+		NativeType{proto: protoVersion3, typ: TypeVarint},
 		[]byte("\x00"),
 		0,
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarint},
+		NativeType{proto: protoVersion3, typ: TypeVarint},
 		[]byte("\x37\xE2\x3C\xEC"),
 		int32(937573612),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarint},
+		NativeType{proto: protoVersion3, typ: TypeVarint},
 		[]byte("\x37\xE2\x3C\xEC"),
 		big.NewInt(937573612),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarint},
+		NativeType{proto: protoVersion3, typ: TypeVarint},
 		[]byte("\x03\x9EV \x15\f\x03\x9DK\x18\xCDI\\$?\a["),
 		bigintize("1231312312331283012830129382342342412123"), // From the iconara/cql-rb test suite
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarint},
+		NativeType{proto: protoVersion3, typ: TypeVarint},
 		[]byte("\xC9v\x8D:\x86"),
 		big.NewInt(-234234234234), // From the iconara/cql-rb test suite
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarint},
+		NativeType{proto: protoVersion3, typ: TypeVarint},
 		[]byte("f\x1e\xfd\xf2\xe3\xb1\x9f|\x04_\x15"),
 		bigintize("123456789123456789123456789"), // From the datastax/python-driver test suite
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarint},
+		NativeType{proto: protoVersion3, typ: TypeVarint},
 		[]byte(nil),
 		nil,
 		nil,
 		UnmarshalError("can not unmarshal into non-pointer <nil>"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeInet},
+		NativeType{proto: protoVersion3, typ: TypeInet},
 		[]byte("\x7F\x00\x00\x01"),
 		net.ParseIP("127.0.0.1").To4(),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInet},
+		NativeType{proto: protoVersion3, typ: TypeInet},
 		[]byte("\xFF\xFF\xFF\xFF"),
 		net.ParseIP("255.255.255.255").To4(),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInet},
+		NativeType{proto: protoVersion3, typ: TypeInet},
 		[]byte("\x7F\x00\x00\x01"),
 		"127.0.0.1",
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInet},
+		NativeType{proto: protoVersion3, typ: TypeInet},
 		[]byte("\xFF\xFF\xFF\xFF"),
 		"255.255.255.255",
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInet},
+		NativeType{proto: protoVersion3, typ: TypeInet},
 		[]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",
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInet},
+		NativeType{proto: protoVersion3, typ: TypeInet},
 		[]byte("\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x02\xb3\xff\xfe\x1e\x83\x29"),
 		"fe80::202:b3ff:fe1e:8329",
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInet},
+		NativeType{proto: protoVersion3, typ: TypeInet},
 		[]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"),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInet},
+		NativeType{proto: protoVersion3, typ: TypeInet},
 		[]byte("\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x02\xb3\xff\xfe\x1e\x83\x29"),
 		net.ParseIP("fe80::202:b3ff:fe1e:8329"),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte(nil),
 		nil,
 		nil,
 		UnmarshalError("can not unmarshal into non-pointer <nil>"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarchar},
+		NativeType{proto: protoVersion3, typ: TypeVarchar},
 		[]byte("nullable string"),
 		func() *string {
 			value := "nullable string"
@@ -618,14 +618,14 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarchar},
+		NativeType{proto: protoVersion3, typ: TypeVarchar},
 		[]byte(nil),
 		(*string)(nil),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x7f\xff\xff\xff"),
 		func() *int {
 			var value int = math.MaxInt32
@@ -635,28 +635,28 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte(nil),
 		(*int)(nil),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTimeUUID},
+		NativeType{proto: protoVersion3, typ: TypeTimeUUID},
 		[]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},
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTimeUUID},
+		NativeType{proto: protoVersion3, typ: TypeTimeUUID},
 		[]byte(nil),
 		(*UUID)(nil),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTimestamp},
+		NativeType{proto: protoVersion3, typ: TypeTimestamp},
 		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 		func() *time.Time {
 			t := time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC)
@@ -666,14 +666,14 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTimestamp},
+		NativeType{proto: protoVersion3, typ: TypeTimestamp},
 		[]byte(nil),
 		(*time.Time)(nil),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBoolean},
+		NativeType{proto: protoVersion3, typ: TypeBoolean},
 		[]byte("\x00"),
 		func() *bool {
 			b := false
@@ -683,7 +683,7 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBoolean},
+		NativeType{proto: protoVersion3, typ: TypeBoolean},
 		[]byte("\x01"),
 		func() *bool {
 			b := true
@@ -693,14 +693,14 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBoolean},
+		NativeType{proto: protoVersion3, typ: TypeBoolean},
 		[]byte(nil),
 		(*bool)(nil),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeFloat},
+		NativeType{proto: protoVersion3, typ: TypeFloat},
 		[]byte("\x40\x49\x0f\xdb"),
 		func() *float32 {
 			f := float32(3.14159265)
@@ -710,14 +710,14 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeFloat},
+		NativeType{proto: protoVersion3, typ: TypeFloat},
 		[]byte(nil),
 		(*float32)(nil),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDouble},
+		NativeType{proto: protoVersion3, typ: TypeDouble},
 		[]byte("\x40\x09\x21\xfb\x53\xc8\xd4\xf1"),
 		func() *float64 {
 			d := float64(3.14159265)
@@ -727,14 +727,14 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeDouble},
+		NativeType{proto: protoVersion3, typ: TypeDouble},
 		[]byte(nil),
 		(*float64)(nil),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInet},
+		NativeType{proto: protoVersion3, typ: TypeInet},
 		[]byte("\x7F\x00\x00\x01"),
 		func() *net.IP {
 			ip := net.ParseIP("127.0.0.1").To4()
@@ -744,7 +744,7 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInet},
+		NativeType{proto: protoVersion3, typ: TypeInet},
 		[]byte(nil),
 		(*net.IP)(nil),
 		nil,
@@ -752,10 +752,10 @@ var marshalTests = []struct {
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeList},
-			Elem:       NativeType{proto: 2, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeList},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
-		[]byte("\x00\x02\x00\x04\x00\x00\x00\x01\x00\x04\x00\x00\x00\x02"),
+		[]byte("\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02"),
 		func() *[]int {
 			l := []int{1, 2}
 			return &l
@@ -765,8 +765,8 @@ var marshalTests = []struct {
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 3, typ: TypeList},
-			Elem:       NativeType{proto: 3, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeList},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
 		[]byte("\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02"),
 		func() *[]int {
@@ -778,8 +778,8 @@ var marshalTests = []struct {
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeList},
-			Elem:       NativeType{proto: 2, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeList},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
 		[]byte(nil),
 		(*[]int)(nil),
@@ -788,11 +788,11 @@ var marshalTests = []struct {
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeMap},
-			Key:        NativeType{proto: 2, typ: TypeVarchar},
-			Elem:       NativeType{proto: 2, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeMap},
+			Key:        NativeType{proto: protoVersion3, typ: TypeVarchar},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
-		[]byte("\x00\x01\x00\x03foo\x00\x04\x00\x00\x00\x01"),
+		[]byte("\x00\x00\x00\x01\x00\x00\x00\x03foo\x00\x00\x00\x04\x00\x00\x00\x01"),
 		func() *map[string]int {
 			m := map[string]int{"foo": 1}
 			return &m
@@ -802,9 +802,9 @@ var marshalTests = []struct {
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeMap},
-			Key:        NativeType{proto: 2, typ: TypeVarchar},
-			Elem:       NativeType{proto: 2, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeMap},
+			Key:        NativeType{proto: protoVersion3, typ: TypeVarchar},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
 		[]byte(nil),
 		(*map[string]int)(nil),
@@ -812,7 +812,7 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarchar},
+		NativeType{proto: protoVersion3, typ: TypeVarchar},
 		[]byte("HELLO WORLD"),
 		func() *CustomString {
 			customString := CustomString("hello world")
@@ -822,252 +822,252 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarchar},
+		NativeType{proto: protoVersion3, typ: TypeVarchar},
 		[]byte(nil),
 		(*CustomString)(nil),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\x7f\xff"),
 		32767, // math.MaxInt16
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\x7f\xff"),
 		"32767", // math.MaxInt16
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\x00\x01"),
 		int16(1),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\xff\xff"),
 		int16(-1),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\x00\xff"),
 		uint8(255),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\xff\xff"),
 		uint16(65535),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\xff\xff"),
 		uint32(65535),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\xff\xff"),
 		uint64(65535),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\x00\xff"),
 		AliasUint8(255),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\xff\xff"),
 		AliasUint16(65535),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\xff\xff"),
 		AliasUint32(65535),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\xff\xff"),
 		AliasUint64(65535),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\xff\xff"),
 		AliasUint(65535),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\x7f"),
 		127, // math.MaxInt8
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\x7f"),
 		"127", // math.MaxInt8
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\x01"),
 		int16(1),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\xff"),
 		int16(-1),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\xff"),
 		uint8(255),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\xff"),
 		uint64(255),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\xff"),
 		uint32(255),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\xff"),
 		uint16(255),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\xff"),
 		uint(255),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\xff"),
 		AliasUint8(255),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\xff"),
 		AliasUint64(255),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\xff"),
 		AliasUint32(255),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\xff"),
 		AliasUint16(255),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTinyInt},
+		NativeType{proto: protoVersion3, typ: TypeTinyInt},
 		[]byte("\xff"),
 		AliasUint(255),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\x00\x00\x00\xff"),
 		uint8(math.MaxUint8),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\x00\x00\xff\xff"),
 		uint64(math.MaxUint16),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\xff\xff\xff\xff"),
 		uint64(math.MaxUint32),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\xff\xff\xff\xff\xff\xff\xff\xff"),
 		uint64(math.MaxUint64),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\xff\xff\xff\xff"),
 		uint32(math.MaxUint32),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\xff\xff\xff\xff"),
 		uint64(math.MaxUint32),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeBlob},
+		NativeType{proto: protoVersion3, typ: TypeBlob},
 		[]byte(nil),
 		([]byte)(nil),
 		nil,
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeVarchar},
+		NativeType{proto: protoVersion3, typ: TypeVarchar},
 		[]byte{},
 		func() interface{} {
 			var s string
@@ -1077,7 +1077,7 @@ var marshalTests = []struct {
 		nil,
 	},
 	{
-		NativeType{proto: 2, typ: TypeTime},
+		NativeType{proto: protoVersion3, typ: TypeTime},
 		encBigInt(1000),
 		time.Duration(1000),
 		nil,
@@ -1092,177 +1092,177 @@ var unmarshalTests = []struct {
 	UnmarshalError error
 }{
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\xff\xff"),
 		uint8(0),
 		UnmarshalError("unmarshal int: value -1 out of range for uint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\x01\x00"),
 		uint8(0),
 		UnmarshalError("unmarshal int: value 256 out of range for uint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\xff\xff\xff\xff"),
 		uint8(0),
 		UnmarshalError("unmarshal int: value -1 out of range for uint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x00\x00\x01\x00"),
 		uint8(0),
 		UnmarshalError("unmarshal int: value 256 out of range for uint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\xff\xff\xff\xff"),
 		uint16(0),
 		UnmarshalError("unmarshal int: value -1 out of range for uint16"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x00\x01\x00\x00"),
 		uint16(0),
 		UnmarshalError("unmarshal int: value 65536 out of range for uint16"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\xff\xff\xff\xff\xff\xff\xff\xff"),
 		uint8(0),
 		UnmarshalError("unmarshal int: value -1 out of range for uint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\x00\x00\x01\x00"),
 		uint8(0),
 		UnmarshalError("unmarshal int: value 256 out of range for uint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\xff\xff\xff\xff\xff\xff\xff\xff"),
 		uint8(0),
 		UnmarshalError("unmarshal int: value -1 out of range for uint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\x00\x00\x01\x00"),
 		uint8(0),
 		UnmarshalError("unmarshal int: value 256 out of range for uint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\xff\xff\xff\xff\xff\xff\xff\xff"),
 		uint16(0),
 		UnmarshalError("unmarshal int: value -1 out of range for uint16"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\x00\x01\x00\x00"),
 		uint16(0),
 		UnmarshalError("unmarshal int: value 65536 out of range for uint16"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\xff\xff\xff\xff\xff\xff\xff\xff"),
 		uint32(0),
 		UnmarshalError("unmarshal int: value -1 out of range for uint32"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x01\x00\x00\x00\x00"),
 		uint32(0),
 		UnmarshalError("unmarshal int: value 4294967296 out of range for uint32"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\xff\xff"),
 		AliasUint8(0),
 		UnmarshalError("unmarshal int: value -1 out of range for gocql.AliasUint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeSmallInt},
+		NativeType{proto: protoVersion3, typ: TypeSmallInt},
 		[]byte("\x01\x00"),
 		AliasUint8(0),
 		UnmarshalError("unmarshal int: value 256 out of range for gocql.AliasUint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\xff\xff\xff\xff"),
 		AliasUint8(0),
 		UnmarshalError("unmarshal int: value -1 out of range for gocql.AliasUint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x00\x00\x01\x00"),
 		AliasUint8(0),
 		UnmarshalError("unmarshal int: value 256 out of range for gocql.AliasUint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\xff\xff\xff\xff"),
 		AliasUint16(0),
 		UnmarshalError("unmarshal int: value -1 out of range for gocql.AliasUint16"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeInt},
+		NativeType{proto: protoVersion3, typ: TypeInt},
 		[]byte("\x00\x01\x00\x00"),
 		AliasUint16(0),
 		UnmarshalError("unmarshal int: value 65536 out of range for gocql.AliasUint16"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\xff\xff\xff\xff\xff\xff\xff\xff"),
 		AliasUint8(0),
 		UnmarshalError("unmarshal int: value -1 out of range for gocql.AliasUint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\x00\x00\x01\x00"),
 		AliasUint8(0),
 		UnmarshalError("unmarshal int: value 256 out of range for gocql.AliasUint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\xff\xff\xff\xff\xff\xff\xff\xff"),
 		AliasUint8(0),
 		UnmarshalError("unmarshal int: value -1 out of range for gocql.AliasUint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\x00\x00\x01\x00"),
 		AliasUint8(0),
 		UnmarshalError("unmarshal int: value 256 out of range for gocql.AliasUint8"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\xff\xff\xff\xff\xff\xff\xff\xff"),
 		AliasUint16(0),
 		UnmarshalError("unmarshal int: value -1 out of range for gocql.AliasUint16"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x00\x00\x01\x00\x00"),
 		AliasUint16(0),
 		UnmarshalError("unmarshal int: value 65536 out of range for gocql.AliasUint16"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\xff\xff\xff\xff\xff\xff\xff\xff"),
 		AliasUint32(0),
 		UnmarshalError("unmarshal int: value -1 out of range for gocql.AliasUint32"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeBigInt},
+		NativeType{proto: protoVersion3, typ: TypeBigInt},
 		[]byte("\x00\x00\x00\x01\x00\x00\x00\x00"),
 		AliasUint32(0),
 		UnmarshalError("unmarshal int: value 4294967296 out of range for gocql.AliasUint32"),
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 3, typ: TypeList},
-			Elem:       NativeType{proto: 3, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeList},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
 		[]byte("\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00"), // truncated data
 		func() *[]int {
@@ -1273,56 +1273,56 @@ var unmarshalTests = []struct {
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeMap},
-			Key:        NativeType{proto: 2, typ: TypeVarchar},
-			Elem:       NativeType{proto: 2, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeMap},
+			Key:        NativeType{proto: protoVersion3, typ: TypeVarchar},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
-		[]byte("\x00\x01\x00\x03fo"),
+		[]byte("\x00\x00\x00\x01\x00\x00\x00\x03fo"),
 		map[string]int{"foo": 1},
 		UnmarshalError("unmarshal map: unexpected eof"),
 	},
 	{
 		CollectionType{
-			NativeType: NativeType{proto: 2, typ: TypeMap},
-			Key:        NativeType{proto: 2, typ: TypeVarchar},
-			Elem:       NativeType{proto: 2, typ: TypeInt},
+			NativeType: NativeType{proto: protoVersion3, typ: TypeMap},
+			Key:        NativeType{proto: protoVersion3, typ: TypeVarchar},
+			Elem:       NativeType{proto: protoVersion3, typ: TypeInt},
 		},
-		[]byte("\x00\x01\x00\x03foo\x00\x04\x00\x00"),
+		[]byte("\x00\x00\x00\x01\x00\x00\x00\x03foo\x00\x04\x00\x00"),
 		map[string]int{"foo": 1},
 		UnmarshalError("unmarshal map: unexpected eof"),
 	},
 	{
-		NativeType{proto: 2, typ: TypeDecimal},
+		NativeType{proto: protoVersion3, typ: TypeDecimal},
 		[]byte("\xff\xff\xff"),
 		inf.NewDec(0, 0), // From the datastax/python-driver test suite
 		UnmarshalError("inf.Dec needs at least 4 bytes, while value has only 3"),
 	},
 	{
-		NativeType{proto: 5, typ: TypeDuration},
+		NativeType{proto: protoVersion5, typ: TypeDuration},
 		[]byte("\x89\xa2\xc3\xc2\x9a\xe0F\x91"),
 		Duration{},
 		UnmarshalError("failed to unmarshal duration into *gocql.Duration: failed to extract nanoseconds: data expect to have 9 bytes, but it has only 8"),
 	},
 	{
-		NativeType{proto: 5, typ: TypeDuration},
+		NativeType{proto: protoVersion5, typ: TypeDuration},
 		[]byte("\x89\xa2\xc3\xc2\x9a"),
 		Duration{},
 		UnmarshalError("failed to unmarshal duration into *gocql.Duration: failed to extract nanoseconds: unexpected eof"),
 	},
 	{
-		NativeType{proto: 5, typ: TypeDuration},
+		NativeType{proto: protoVersion5, typ: TypeDuration},
 		[]byte("\x89\xa2\xc3\xc2"),
 		Duration{},
 		UnmarshalError("failed to unmarshal duration into *gocql.Duration: failed to extract days: data expect to have 5 bytes, but it has only 4"),
 	},
 	{
-		NativeType{proto: 5, typ: TypeDuration},
+		NativeType{proto: protoVersion5, typ: TypeDuration},
 		[]byte("\x89\xa2"),
 		Duration{},
 		UnmarshalError("failed to unmarshal duration into *gocql.Duration: failed to extract days: unexpected eof"),
 	},
 	{
-		NativeType{proto: 5, typ: TypeDuration},
+		NativeType{proto: protoVersion5, typ: TypeDuration},
 		[]byte("\x89"),
 		Duration{},
 		UnmarshalError("failed to unmarshal duration into *gocql.Duration: failed to extract month: data expect to have 2 bytes, but it has only 1"),
@@ -1459,7 +1459,7 @@ func TestMarshalVarint(t *testing.T) {
 	}
 
 	for i, test := range varintTests {
-		data, err := Marshal(NativeType{proto: 2, typ: TypeVarint}, test.Value)
+		data, err := Marshal(NativeType{proto: protoVersion3, typ: TypeVarint}, test.Value)
 		if err != nil {
 			t.Errorf("error marshaling varint: %v (test #%d)", err, i)
 		}
@@ -1469,7 +1469,7 @@ func TestMarshalVarint(t *testing.T) {
 		}
 
 		binder := new(big.Int)
-		err = Unmarshal(NativeType{proto: 2, typ: TypeVarint}, test.Marshaled, binder)
+		err = Unmarshal(NativeType{proto: protoVersion3, typ: TypeVarint}, test.Marshaled, binder)
 		if err != nil {
 			t.Errorf("error unmarshaling varint: %v (test #%d)", err, i)
 		}
@@ -1517,7 +1517,7 @@ func TestMarshalVarint(t *testing.T) {
 	}
 
 	for i, test := range varintUint64Tests {
-		data, err := Marshal(NativeType{proto: 2, typ: TypeVarint}, test.Value)
+		data, err := Marshal(NativeType{proto: protoVersion3, typ: TypeVarint}, test.Value)
 		if err != nil {
 			t.Errorf("error marshaling varint: %v (test #%d)", err, i)
 		}
@@ -1527,7 +1527,7 @@ func TestMarshalVarint(t *testing.T) {
 		}
 
 		var binder uint64
-		err = Unmarshal(NativeType{proto: 2, typ: TypeVarint}, test.Marshaled, &binder)
+		err = Unmarshal(NativeType{proto: protoVersion3, typ: TypeVarint}, test.Marshaled, &binder)
 		if err != nil {
 			t.Errorf("error unmarshaling varint to uint64: %v (test #%d)", err, i)
 		}
@@ -1545,12 +1545,12 @@ func TestMarshalBigInt(t *testing.T) {
 		MarshalError error
 	}{
 		{
-			NativeType{proto: 2, typ: TypeBigInt},
+			NativeType{proto: protoVersion3, typ: TypeBigInt},
 			"-78635384813432117863538481343211",
 			MarshalError("can not marshal string to bigint: strconv.ParseInt: parsing \"-78635384813432117863538481343211\": value out of range"),
 		},
 		{
-			NativeType{proto: 2, typ: TypeBigInt},
+			NativeType{proto: protoVersion3, typ: TypeBigInt},
 			"922337203685477692259749625974294",
 			MarshalError("can not marshal string to bigint: strconv.ParseInt: parsing \"922337203685477692259749625974294\": value out of range"),
 		},
@@ -1577,13 +1577,9 @@ func equalStringPointerSlice(leftList, rightList []*string) bool {
 }
 
 func TestMarshalList(t *testing.T) {
-	typeInfoV2 := CollectionType{
-		NativeType: NativeType{proto: 2, typ: TypeList},
-		Elem:       NativeType{proto: 2, typ: TypeVarchar},
-	}
 	typeInfoV3 := CollectionType{
-		NativeType: NativeType{proto: 3, typ: TypeList},
-		Elem:       NativeType{proto: 3, typ: TypeVarchar},
+		NativeType: NativeType{proto: protoVersion3, typ: TypeList},
+		Elem:       NativeType{proto: protoVersion3, typ: TypeVarchar},
 	}
 
 	type tc struct {
@@ -1596,37 +1592,6 @@ func TestMarshalList(t *testing.T) {
 	valueB := "valueB"
 	valueEmpty := ""
 	testCases := []tc{
-		{
-			typeInfo: typeInfoV2,
-			input:    []*string{&valueA},
-			expected: []*string{&valueA},
-		},
-		{
-			typeInfo: typeInfoV2,
-			input:    []*string{&valueA, &valueB},
-			expected: []*string{&valueA, &valueB},
-		},
-		{
-			typeInfo: typeInfoV2,
-			input:    []*string{&valueA, &valueEmpty, &valueB},
-			expected: []*string{&valueA, &valueEmpty, &valueB},
-		},
-		{
-			typeInfo: typeInfoV2,
-			input:    []*string{&valueEmpty},
-			expected: []*string{&valueEmpty},
-		},
-		{
-			// nil values are marshalled to empty values for protocol < 3
-			typeInfo: typeInfoV2,
-			input:    []*string{nil},
-			expected: []*string{&valueEmpty},
-		},
-		{
-			typeInfo: typeInfoV2,
-			input:    []*string{&valueA, nil, &valueB},
-			expected: []*string{&valueA, &valueEmpty, &valueB},
-		},
 		{
 			typeInfo: typeInfoV3,
 			input:    []*string{&valueEmpty},
@@ -1740,7 +1705,7 @@ func (m *MyPointerMarshaler) MarshalCQL(_ TypeInfo) ([]byte, error) {
 
 func TestMarshalPointer(t *testing.T) {
 	m := &MyPointerMarshaler{}
-	typ := NativeType{proto: 2, typ: TypeInt}
+	typ := NativeType{proto: protoVersion3, typ: TypeInt}
 
 	data, err := Marshal(typ, m)
 
@@ -1762,17 +1727,17 @@ func TestMarshalTime(t *testing.T) {
 		Value interface{}
 	}{
 		{
-			NativeType{proto: 4, typ: TypeTime},
+			NativeType{proto: protoVersion4, typ: TypeTime},
 			expectedData,
 			duration.Nanoseconds(),
 		},
 		{
-			NativeType{proto: 4, typ: TypeTime},
+			NativeType{proto: protoVersion4, typ: TypeTime},
 			expectedData,
 			duration,
 		},
 		{
-			NativeType{proto: 4, typ: TypeTime},
+			NativeType{proto: protoVersion4, typ: TypeTime},
 			expectedData,
 			&duration,
 		},
@@ -1799,46 +1764,46 @@ func TestMarshalTimestamp(t *testing.T) {
 		Value interface{}
 	}{
 		{
-			NativeType{proto: 2, typ: TypeTimestamp},
+			NativeType{proto: protoVersion3, typ: TypeTimestamp},
 			[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 			time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC),
 		},
 		{
-			NativeType{proto: 2, typ: TypeTimestamp},
+			NativeType{proto: protoVersion3, typ: TypeTimestamp},
 			[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
 			int64(1376387523000),
 		},
 		{
 			// 9223372036854 is the maximum time representable in ms since the epoch
 			// with int64 if using UnixNano to convert
-			NativeType{proto: 2, typ: TypeTimestamp},
+			NativeType{proto: protoVersion3, typ: TypeTimestamp},
 			[]byte("\x00\x00\x08\x63\x7b\xd0\x5a\xf6"),
 			time.Date(2262, time.April, 11, 23, 47, 16, 854775807, time.UTC),
 		},
 		{
 			// One nanosecond after causes overflow when using UnixNano
 			// Instead it should resolve to the same time in ms
-			NativeType{proto: 2, typ: TypeTimestamp},
+			NativeType{proto: protoVersion3, typ: TypeTimestamp},
 			[]byte("\x00\x00\x08\x63\x7b\xd0\x5a\xf6"),
 			time.Date(2262, time.April, 11, 23, 47, 16, 854775808, time.UTC),
 		},
 		{
 			// -9223372036855 is the minimum time representable in ms since the epoch
 			// with int64 if using UnixNano to convert
-			NativeType{proto: 2, typ: TypeTimestamp},
+			NativeType{proto: protoVersion3, typ: TypeTimestamp},
 			[]byte("\xff\xff\xf7\x9c\x84\x2f\xa5\x09"),
 			time.Date(1677, time.September, 21, 00, 12, 43, 145224192, time.UTC),
 		},
 		{
 			// One nanosecond earlier causes overflow when using UnixNano
 			// it should resolve to the same time in ms
-			NativeType{proto: 2, typ: TypeTimestamp},
+			NativeType{proto: protoVersion3, typ: TypeTimestamp},
 			[]byte("\xff\xff\xf7\x9c\x84\x2f\xa5\x09"),
 			time.Date(1677, time.September, 21, 00, 12, 43, 145224191, time.UTC),
 		},
 		{
 			// Store the zero time as a blank slice
-			NativeType{proto: 2, typ: TypeTimestamp},
+			NativeType{proto: protoVersion3, typ: TypeTimestamp},
 			[]byte{},
 			time.Time{},
 		},
@@ -1860,10 +1825,10 @@ func TestMarshalTimestamp(t *testing.T) {
 
 func TestMarshalTuple(t *testing.T) {
 	info := TupleTypeInfo{
-		NativeType: NativeType{proto: 3, typ: TypeTuple},
+		NativeType: NativeType{proto: protoVersion3, typ: TypeTuple},
 		Elems: []TypeInfo{
-			NativeType{proto: 3, typ: TypeVarchar},
-			NativeType{proto: 3, typ: TypeVarchar},
+			NativeType{proto: protoVersion3, typ: TypeVarchar},
+			NativeType{proto: protoVersion3, typ: TypeVarchar},
 		},
 	}
 
@@ -2007,10 +1972,10 @@ func TestMarshalTuple(t *testing.T) {
 
 func TestUnmarshalTuple(t *testing.T) {
 	info := TupleTypeInfo{
-		NativeType: NativeType{proto: 3, typ: TypeTuple},
+		NativeType: NativeType{proto: protoVersion3, typ: TypeTuple},
 		Elems: []TypeInfo{
-			NativeType{proto: 3, typ: TypeVarchar},
-			NativeType{proto: 3, typ: TypeVarchar},
+			NativeType{proto: protoVersion3, typ: TypeVarchar},
+			NativeType{proto: protoVersion3, typ: TypeVarchar},
 		},
 	}
 
@@ -2081,10 +2046,10 @@ func TestUnmarshalTuple(t *testing.T) {
 }
 
 func TestMarshalUDTMap(t *testing.T) {
-	typeInfo := UDTTypeInfo{NativeType{proto: 3, typ: TypeUDT}, "", "xyz", []UDTField{
-		{Name: "x", Type: NativeType{proto: 3, typ: TypeInt}},
-		{Name: "y", Type: NativeType{proto: 3, typ: TypeInt}},
-		{Name: "z", Type: NativeType{proto: 3, typ: TypeInt}},
+	typeInfo := UDTTypeInfo{NativeType{proto: protoVersion3, typ: TypeUDT}, "", "xyz", []UDTField{
+		{Name: "x", Type: NativeType{proto: protoVersion3, typ: TypeInt}},
+		{Name: "y", Type: NativeType{proto: protoVersion3, typ: TypeInt}},
+		{Name: "z", Type: NativeType{proto: protoVersion3, typ: TypeInt}},
 	}}
 
 	t.Run("partially bound", func(t *testing.T) {
@@ -2136,10 +2101,10 @@ func TestMarshalUDTMap(t *testing.T) {
 }
 
 func TestMarshalUDTStruct(t *testing.T) {
-	typeInfo := UDTTypeInfo{NativeType{proto: 3, typ: TypeUDT}, "", "xyz", []UDTField{
-		{Name: "x", Type: NativeType{proto: 3, typ: TypeInt}},
-		{Name: "y", Type: NativeType{proto: 3, typ: TypeInt}},
-		{Name: "z", Type: NativeType{proto: 3, typ: TypeInt}},
+	typeInfo := UDTTypeInfo{NativeType{proto: protoVersion3, typ: TypeUDT}, "", "xyz", []UDTField{
+		{Name: "x", Type: NativeType{proto: protoVersion3, typ: TypeInt}},
+		{Name: "y", Type: NativeType{proto: protoVersion3, typ: TypeInt}},
+		{Name: "z", Type: NativeType{proto: protoVersion3, typ: TypeInt}},
 	}}
 
 	type xyzStruct struct {
@@ -2224,7 +2189,7 @@ func TestMarshalNil(t *testing.T) {
 	}
 
 	for _, typ := range types {
-		data, err := Marshal(NativeType{proto: 3, typ: typ}, nil)
+		data, err := Marshal(NativeType{proto: protoVersion3, typ: typ}, nil)
 		if err != nil {
 			t.Errorf("unable to marshal nil %v: %v\n", typ, err)
 		} else if data != nil {
@@ -2236,7 +2201,7 @@ func TestMarshalNil(t *testing.T) {
 func TestUnmarshalInetCopyBytes(t *testing.T) {
 	data := []byte{127, 0, 0, 1}
 	var ip net.IP
-	if err := unmarshalInet(NativeType{proto: 2, typ: TypeInet}, data, &ip); err != nil {
+	if err := unmarshalInet(NativeType{proto: protoVersion3, typ: TypeInet}, data, &ip); err != nil {
 		t.Fatal(err)
 	}
 
@@ -2250,7 +2215,7 @@ func TestUnmarshalInetCopyBytes(t *testing.T) {
 func TestUnmarshalDate(t *testing.T) {
 	data := []uint8{0x80, 0x0, 0x43, 0x31}
 	var date time.Time
-	if err := unmarshalDate(NativeType{proto: 2, typ: TypeDate}, data, &date); err != nil {
+	if err := unmarshalDate(NativeType{proto: protoVersion3, typ: TypeDate}, data, &date); err != nil {
 		t.Fatal(err)
 	}
 
@@ -2261,7 +2226,7 @@ func TestUnmarshalDate(t *testing.T) {
 		return
 	}
 	var stringDate string
-	if err2 := unmarshalDate(NativeType{proto: 2, typ: TypeDate}, data, &stringDate); err2 != nil {
+	if err2 := unmarshalDate(NativeType{proto: protoVersion3, typ: TypeDate}, data, &stringDate); err2 != nil {
 		t.Fatal(err2)
 	}
 	if expectedDate != stringDate {
@@ -2281,22 +2246,22 @@ func TestMarshalDate(t *testing.T) {
 		Value interface{}
 	}{
 		{
-			NativeType{proto: 4, typ: TypeDate},
+			NativeType{proto: protoVersion4, typ: TypeDate},
 			expectedData,
 			timestamp,
 		},
 		{
-			NativeType{proto: 4, typ: TypeDate},
+			NativeType{proto: protoVersion4, typ: TypeDate},
 			expectedData,
 			now,
 		},
 		{
-			NativeType{proto: 4, typ: TypeDate},
+			NativeType{proto: protoVersion4, typ: TypeDate},
 			expectedData,
 			&now,
 		},
 		{
-			NativeType{proto: 4, typ: TypeDate},
+			NativeType{proto: protoVersion4, typ: TypeDate},
 			expectedData,
 			now.Format("2006-01-02"),
 		},
@@ -2340,7 +2305,7 @@ func TestLargeDate(t *testing.T) {
 		},
 	}
 
-	nativeType := NativeType{proto: 4, typ: TypeDate}
+	nativeType := NativeType{proto: protoVersion4, typ: TypeDate}
 
 	for i, test := range marshalDateTests {
 		t.Log(i, test)
@@ -2390,22 +2355,22 @@ func TestMarshalDuration(t *testing.T) {
 		Value interface{}
 	}{
 		{
-			NativeType{proto: 5, typ: TypeDuration},
+			NativeType{proto: protoVersion5, typ: TypeDuration},
 			expectedData,
 			duration.Nanoseconds(),
 		},
 		{
-			NativeType{proto: 5, typ: TypeDuration},
+			NativeType{proto: protoVersion5, typ: TypeDuration},
 			expectedData,
 			duration,
 		},
 		{
-			NativeType{proto: 5, typ: TypeDuration},
+			NativeType{proto: protoVersion5, typ: TypeDuration},
 			expectedData,
 			durationS,
 		},
 		{
-			NativeType{proto: 5, typ: TypeDuration},
+			NativeType{proto: protoVersion5, typ: TypeDuration},
 			expectedData,
 			&duration,
 		},
@@ -2426,13 +2391,9 @@ func TestMarshalDuration(t *testing.T) {
 }
 
 func TestReadCollectionSize(t *testing.T) {
-	listV2 := CollectionType{
-		NativeType: NativeType{proto: 2, typ: TypeList},
-		Elem:       NativeType{proto: 2, typ: TypeVarchar},
-	}
 	listV3 := CollectionType{
-		NativeType: NativeType{proto: 3, typ: TypeList},
-		Elem:       NativeType{proto: 3, typ: TypeVarchar},
+		NativeType: NativeType{proto: protoVersion3, typ: TypeList},
+		Elem:       NativeType{proto: protoVersion3, typ: TypeVarchar},
 	}
 
 	tests := []struct {
@@ -2442,24 +2403,6 @@ func TestReadCollectionSize(t *testing.T) {
 		isError      bool
 		expectedSize int
 	}{
-		{
-			name:    "short read 0 proto 2",
-			info:    listV2,
-			data:    []byte{},
-			isError: true,
-		},
-		{
-			name:    "short read 1 proto 2",
-			info:    listV2,
-			data:    []byte{0x01},
-			isError: true,
-		},
-		{
-			name:         "good read proto 2",
-			info:         listV2,
-			data:         []byte{0x01, 0x38},
-			expectedSize: 0x0138,
-		},
 		{
 			name:    "short read 0 proto 3",
 			info:    listV3,
@@ -2493,7 +2436,7 @@ func TestReadCollectionSize(t *testing.T) {
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			size, _, err := readCollectionSize(test.info, test.data)
+			size, _, err := readCollectionSize(test.data)
 			if test.isError {
 				if err == nil {
 					t.Fatal("Expected error, but it was nil")
@@ -2557,17 +2500,17 @@ func BenchmarkUnmarshalUUID(b *testing.B) {
 
 func TestUnmarshalUDT(t *testing.T) {
 	info := UDTTypeInfo{
-		NativeType: NativeType{proto: 4, typ: TypeUDT},
+		NativeType: NativeType{proto: protoVersion4, typ: TypeUDT},
 		Name:       "myudt",
 		KeySpace:   "myks",
 		Elements: []UDTField{
 			{
 				Name: "first",
-				Type: NativeType{proto: 4, typ: TypeAscii},
+				Type: NativeType{proto: protoVersion4, typ: TypeAscii},
 			},
 			{
 				Name: "second",
-				Type: NativeType{proto: 4, typ: TypeSmallInt},
+				Type: NativeType{proto: protoVersion4, typ: TypeSmallInt},
 			},
 		},
 	}
diff --git a/metadata_test.go b/metadata_test.go
index 6b3d119..21829b3 100644
--- a/metadata_test.go
+++ b/metadata_test.go
@@ -38,310 +38,11 @@ import (
 func TestCompileMetadata(t *testing.T) {
 	// V1 tests - these are all based on real examples from the integration test ccm cluster
 	log := &defaultLogger{}
-	keyspace := &KeyspaceMetadata{
-		Name: "V1Keyspace",
-	}
-	tables := []TableMetadata{
-		{
-			// This table, found in the system keyspace, has no key aliases or column aliases
-			Keyspace:         "V1Keyspace",
-			Name:             "Schema",
-			KeyValidator:     "org.apache.cassandra.db.marshal.BytesType",
-			Comparator:       "org.apache.cassandra.db.marshal.UTF8Type",
-			DefaultValidator: "org.apache.cassandra.db.marshal.BytesType",
-			KeyAliases:       []string{},
-			ColumnAliases:    []string{},
-			ValueAlias:       "",
-		},
-		{
-			// This table, found in the system keyspace, has key aliases, column aliases, and a value alias.
-			Keyspace:         "V1Keyspace",
-			Name:             "hints",
-			KeyValidator:     "org.apache.cassandra.db.marshal.UUIDType",
-			Comparator:       "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.TimeUUIDType,org.apache.cassandra.db.marshal.Int32Type)",
-			DefaultValidator: "org.apache.cassandra.db.marshal.BytesType",
-			KeyAliases:       []string{"target_id"},
-			ColumnAliases:    []string{"hint_id", "message_version"},
-			ValueAlias:       "mutation",
-		},
-		{
-			// This table, found in the system keyspace, has a comparator with collections, but no column aliases
-			Keyspace:         "V1Keyspace",
-			Name:             "peers",
-			KeyValidator:     "org.apache.cassandra.db.marshal.InetAddressType",
-			Comparator:       "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.UTF8Type,org.apache.cassandra.db.marshal.ColumnToCollectionType(746f6b656e73:org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.UTF8Type)))",
-			DefaultValidator: "org.apache.cassandra.db.marshal.BytesType",
-			KeyAliases:       []string{"peer"},
-			ColumnAliases:    []string{},
-			ValueAlias:       "",
-		},
-		{
-			// This table, found in the system keyspace, has a column alias, but not a composite comparator
-			Keyspace:         "V1Keyspace",
-			Name:             "IndexInfo",
-			KeyValidator:     "org.apache.cassandra.db.marshal.UTF8Type",
-			Comparator:       "org.apache.cassandra.db.marshal.ReversedType(org.apache.cassandra.db.marshal.UTF8Type)",
-			DefaultValidator: "org.apache.cassandra.db.marshal.BytesType",
-			KeyAliases:       []string{"table_name"},
-			ColumnAliases:    []string{"index_name"},
-			ValueAlias:       "",
-		},
-		{
-			// This table, found in the gocql_test keyspace following an integration test run, has a composite comparator with collections as well as a column alias
-			Keyspace:         "V1Keyspace",
-			Name:             "wiki_page",
-			KeyValidator:     "org.apache.cassandra.db.marshal.UTF8Type",
-			Comparator:       "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.TimeUUIDType,org.apache.cassandra.db.marshal.UTF8Type,org.apache.cassandra.db.marshal.ColumnToCollectionType(74616773:org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.UTF8Type),6174746163686d656e7473:org.apache.cassandra.db.marshal.MapType(org.apache.cassandra.db.marshal.UTF8Type,org.apache.cassandra.db.marshal.BytesType)))",
-			DefaultValidator: "org.apache.cassandra.db.marshal.BytesType",
-			KeyAliases:       []string{"title"},
-			ColumnAliases:    []string{"revid"},
-			ValueAlias:       "",
-		},
-		{
-			// This is a made up example with multiple unnamed aliases
-			Keyspace:         "V1Keyspace",
-			Name:             "no_names",
-			KeyValidator:     "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.UUIDType,org.apache.cassandra.db.marshal.UUIDType)",
-			Comparator:       "org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.Int32Type,org.apache.cassandra.db.marshal.Int32Type,org.apache.cassandra.db.marshal.Int32Type)",
-			DefaultValidator: "org.apache.cassandra.db.marshal.BytesType",
-			KeyAliases:       []string{},
-			ColumnAliases:    []string{},
-			ValueAlias:       "",
-		},
-	}
-	columns := []ColumnMetadata{
-		// Here are the regular columns from the peers table for testing regular columns
-		{Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "data_center", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type"},
-		{Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "host_id", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UUIDType"},
-		{Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "rack", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type"},
-		{Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "release_version", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type"},
-		{Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "rpc_address", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.InetAddressType"},
-		{Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "schema_version", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UUIDType"},
-		{Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "tokens", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.UTF8Type)"},
-	}
-	compileMetadata(1, keyspace, tables, columns, nil, nil, nil, nil, log)
-	assertKeyspaceMetadata(
-		t,
-		keyspace,
-		&KeyspaceMetadata{
-			Name: "V1Keyspace",
-			Tables: map[string]*TableMetadata{
-				"Schema": {
-					PartitionKey: []*ColumnMetadata{
-						{
-							Name: "key",
-							Type: NativeType{typ: TypeBlob},
-						},
-					},
-					ClusteringColumns: []*ColumnMetadata{},
-					Columns: map[string]*ColumnMetadata{
-						"key": {
-							Name: "key",
-							Type: NativeType{typ: TypeBlob},
-							Kind: ColumnPartitionKey,
-						},
-					},
-				},
-				"hints": {
-					PartitionKey: []*ColumnMetadata{
-						{
-							Name: "target_id",
-							Type: NativeType{typ: TypeUUID},
-						},
-					},
-					ClusteringColumns: []*ColumnMetadata{
-						{
-							Name:  "hint_id",
-							Type:  NativeType{typ: TypeTimeUUID},
-							Order: ASC,
-						},
-						{
-							Name:  "message_version",
-							Type:  NativeType{typ: TypeInt},
-							Order: ASC,
-						},
-					},
-					Columns: map[string]*ColumnMetadata{
-						"target_id": {
-							Name: "target_id",
-							Type: NativeType{typ: TypeUUID},
-							Kind: ColumnPartitionKey,
-						},
-						"hint_id": {
-							Name:  "hint_id",
-							Type:  NativeType{typ: TypeTimeUUID},
-							Order: ASC,
-							Kind:  ColumnClusteringKey,
-						},
-						"message_version": {
-							Name:  "message_version",
-							Type:  NativeType{typ: TypeInt},
-							Order: ASC,
-							Kind:  ColumnClusteringKey,
-						},
-						"mutation": {
-							Name: "mutation",
-							Type: NativeType{typ: TypeBlob},
-							Kind: ColumnRegular,
-						},
-					},
-				},
-				"peers": {
-					PartitionKey: []*ColumnMetadata{
-						{
-							Name: "peer",
-							Type: NativeType{typ: TypeInet},
-						},
-					},
-					ClusteringColumns: []*ColumnMetadata{},
-					Columns: map[string]*ColumnMetadata{
-						"peer": {
-							Name: "peer",
-							Type: NativeType{typ: TypeInet},
-							Kind: ColumnPartitionKey,
-						},
-						"data_center":     {Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "data_center", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type", Type: NativeType{typ: TypeVarchar}},
-						"host_id":         {Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "host_id", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UUIDType", Type: NativeType{typ: TypeUUID}},
-						"rack":            {Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "rack", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type", Type: NativeType{typ: TypeVarchar}},
-						"release_version": {Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "release_version", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UTF8Type", Type: NativeType{typ: TypeVarchar}},
-						"rpc_address":     {Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "rpc_address", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.InetAddressType", Type: NativeType{typ: TypeInet}},
-						"schema_version":  {Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "schema_version", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.UUIDType", Type: NativeType{typ: TypeUUID}},
-						"tokens":          {Keyspace: "V1Keyspace", Table: "peers", Kind: ColumnRegular, Name: "tokens", ComponentIndex: 0, Validator: "org.apache.cassandra.db.marshal.SetType(org.apache.cassandra.db.marshal.UTF8Type)", Type: CollectionType{NativeType: NativeType{typ: TypeSet}}},
-					},
-				},
-				"IndexInfo": {
-					PartitionKey: []*ColumnMetadata{
-						{
-							Name: "table_name",
-							Type: NativeType{typ: TypeVarchar},
-						},
-					},
-					ClusteringColumns: []*ColumnMetadata{
-						{
-							Name:  "index_name",
-							Type:  NativeType{typ: TypeVarchar},
-							Order: DESC,
-						},
-					},
-					Columns: map[string]*ColumnMetadata{
-						"table_name": {
-							Name: "table_name",
-							Type: NativeType{typ: TypeVarchar},
-							Kind: ColumnPartitionKey,
-						},
-						"index_name": {
-							Name:  "index_name",
-							Type:  NativeType{typ: TypeVarchar},
-							Order: DESC,
-							Kind:  ColumnClusteringKey,
-						},
-						"value": {
-							Name: "value",
-							Type: NativeType{typ: TypeBlob},
-							Kind: ColumnRegular,
-						},
-					},
-				},
-				"wiki_page": {
-					PartitionKey: []*ColumnMetadata{
-						{
-							Name: "title",
-							Type: NativeType{typ: TypeVarchar},
-						},
-					},
-					ClusteringColumns: []*ColumnMetadata{
-						{
-							Name:  "revid",
-							Type:  NativeType{typ: TypeTimeUUID},
-							Order: ASC,
-						},
-					},
-					Columns: map[string]*ColumnMetadata{
-						"title": {
-							Name: "title",
-							Type: NativeType{typ: TypeVarchar},
-							Kind: ColumnPartitionKey,
-						},
-						"revid": {
-							Name: "revid",
-							Type: NativeType{typ: TypeTimeUUID},
-							Kind: ColumnClusteringKey,
-						},
-					},
-				},
-				"no_names": {
-					PartitionKey: []*ColumnMetadata{
-						{
-							Name: "key",
-							Type: NativeType{typ: TypeUUID},
-						},
-						{
-							Name: "key2",
-							Type: NativeType{typ: TypeUUID},
-						},
-					},
-					ClusteringColumns: []*ColumnMetadata{
-						{
-							Name:  "column",
-							Type:  NativeType{typ: TypeInt},
-							Order: ASC,
-						},
-						{
-							Name:  "column2",
-							Type:  NativeType{typ: TypeInt},
-							Order: ASC,
-						},
-						{
-							Name:  "column3",
-							Type:  NativeType{typ: TypeInt},
-							Order: ASC,
-						},
-					},
-					Columns: map[string]*ColumnMetadata{
-						"key": {
-							Name: "key",
-							Type: NativeType{typ: TypeUUID},
-							Kind: ColumnPartitionKey,
-						},
-						"key2": {
-							Name: "key2",
-							Type: NativeType{typ: TypeUUID},
-							Kind: ColumnPartitionKey,
-						},
-						"column": {
-							Name:  "column",
-							Type:  NativeType{typ: TypeInt},
-							Order: ASC,
-							Kind:  ColumnClusteringKey,
-						},
-						"column2": {
-							Name:  "column2",
-							Type:  NativeType{typ: TypeInt},
-							Order: ASC,
-							Kind:  ColumnClusteringKey,
-						},
-						"column3": {
-							Name:  "column3",
-							Type:  NativeType{typ: TypeInt},
-							Order: ASC,
-							Kind:  ColumnClusteringKey,
-						},
-						"value": {
-							Name: "value",
-							Type: NativeType{typ: TypeBlob},
-							Kind: ColumnRegular,
-						},
-					},
-				},
-			},
-		},
-	)
-
 	// V2 test - V2+ protocol is simpler so here are some toy examples to verify that the mapping works
-	keyspace = &KeyspaceMetadata{
+	keyspace := &KeyspaceMetadata{
 		Name: "V2Keyspace",
 	}
-	tables = []TableMetadata{
+	tables := []TableMetadata{
 		{
 			Keyspace: "V2Keyspace",
 			Name:     "Table1",
@@ -351,7 +52,7 @@ func TestCompileMetadata(t *testing.T) {
 			Name:     "Table2",
 		},
 	}
-	columns = []ColumnMetadata{
+	columns := []ColumnMetadata{
 		{
 			Keyspace:       "V2Keyspace",
 			Table:          "Table1",
diff --git a/tuple_test.go b/tuple_test.go
index 296c56f..de5317b 100644
--- a/tuple_test.go
+++ b/tuple_test.go
@@ -35,9 +35,6 @@ import (
 func TestTupleSimple(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("tuple types are only available of proto>=3")
-	}
 
 	err := createTable(session, `CREATE TABLE gocql_test.tuple_test(
 		id int,
@@ -79,9 +76,6 @@ func TestTupleSimple(t *testing.T) {
 func TestTuple_NullTuple(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("tuple types are only available of proto>=3")
-	}
 
 	err := createTable(session, `CREATE TABLE gocql_test.tuple_nil_test(
 		id int,
@@ -117,9 +111,6 @@ func TestTuple_NullTuple(t *testing.T) {
 func TestTuple_TupleNotSet(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("tuple types are only available of proto>=3")
-	}
 
 	err := createTable(session, `CREATE TABLE gocql_test.tuple_not_set_test(
 		id int,
@@ -170,9 +161,6 @@ func TestTuple_TupleNotSet(t *testing.T) {
 func TestTupleMapScan(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("tuple types are only available of proto>=3")
-	}
 
 	err := createTable(session, `CREATE TABLE gocql_test.tuple_map_scan(
 		id int,
@@ -203,9 +191,7 @@ func TestTupleMapScan(t *testing.T) {
 func TestTupleMapScanNil(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("tuple types are only available of proto>=3")
-	}
+
 	err := createTable(session, `CREATE TABLE gocql_test.tuple_map_scan_nil(
 			id int,
 			val frozen<tuple<int, int>>,
@@ -234,9 +220,7 @@ func TestTupleMapScanNil(t *testing.T) {
 func TestTupleMapScanNotSet(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("tuple types are only available of proto>=3")
-	}
+
 	err := createTable(session, `CREATE TABLE gocql_test.tuple_map_scan_not_set(
 			id int,
 			val frozen<tuple<int, int>>,
@@ -266,9 +250,7 @@ func TestTupleLastFieldEmpty(t *testing.T) {
 	// Regression test - empty value used to be treated as NULL value in the last tuple field
 	session := createSession(t)
 	defer session.Close()
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("tuple types are only available of proto>=3")
-	}
+
 	err := createTable(session, `CREATE TABLE gocql_test.tuple_last_field_empty(
 			id int,
 			val frozen<tuple<text, text>>,
@@ -304,9 +286,6 @@ func TestTupleLastFieldEmpty(t *testing.T) {
 func TestTuple_NestedCollection(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("tuple types are only available of proto>=3")
-	}
 
 	err := createTable(session, `CREATE TABLE gocql_test.nested_tuples(
 		id int,
@@ -356,9 +335,6 @@ func TestTuple_NestedCollection(t *testing.T) {
 func TestTuple_NullableNestedCollection(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("tuple types are only available of proto>=3")
-	}
 
 	err := createTable(session, `CREATE TABLE gocql_test.nested_tuples_with_nulls(
 		id int,
diff --git a/udt_test.go b/udt_test.go
index f1980f2..65ee10b 100644
--- a/udt_test.go
+++ b/udt_test.go
@@ -72,10 +72,6 @@ func TestUDT_Marshaler(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
 
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("UDT are only available on protocol >= 3")
-	}
-
 	err := createTable(session, `CREATE TYPE gocql_test.position(
 		lat int,
 		lon int,
@@ -129,10 +125,6 @@ func TestUDT_Reflect(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
 
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("UDT are only available on protocol >= 3")
-	}
-
 	err := createTable(session, `CREATE TYPE gocql_test.horse(
 		name text,
 		owner text);`)
@@ -176,22 +168,10 @@ func TestUDT_Reflect(t *testing.T) {
 	}
 }
 
-func TestUDT_Proto2error(t *testing.T) {
-	// TODO(zariel): move this to marshal test?
-	_, err := Marshal(NativeType{custom: "org.apache.cassandra.db.marshal.UserType.Type", proto: 2}, 1)
-	if err != ErrorUDTUnavailable {
-		t.Fatalf("expected %v got %v", ErrUnavailable, err)
-	}
-}
-
 func TestUDT_NullObject(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
 
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("UDT are only available on protocol >= 3")
-	}
-
 	err := createTable(session, `CREATE TYPE gocql_test.udt_null_type(
 		name text,
 		owner text);`)
@@ -242,10 +222,6 @@ func TestMapScanUDT(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
 
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("UDT are only available on protocol >= 3")
-	}
-
 	err := createTable(session, `CREATE TYPE gocql_test.log_entry (
 		created_timestamp timestamp,
 		message text
@@ -329,10 +305,6 @@ func TestUDT_MissingField(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
 
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("UDT are only available on protocol >= 3")
-	}
-
 	err := createTable(session, `CREATE TYPE gocql_test.missing_field(
 		name text,
 		owner text);`)
@@ -379,10 +351,6 @@ func TestUDT_EmptyCollections(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
 
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("UDT are only available on protocol >= 3")
-	}
-
 	err := createTable(session, `CREATE TYPE gocql_test.nil_collections(
 		a list<text>,
 		b map<text, text>,
@@ -435,10 +403,6 @@ func TestUDT_UpdateField(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
 
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("UDT are only available on protocol >= 3")
-	}
-
 	err := createTable(session, `CREATE TYPE gocql_test.update_field_udt(
 		name text,
 		owner text);`)
@@ -492,10 +456,6 @@ func TestUDT_ScanNullUDT(t *testing.T) {
 	session := createSession(t)
 	defer session.Close()
 
-	if session.cfg.ProtoVersion < protoVersion3 {
-		t.Skip("UDT are only available on protocol >= 3")
-	}
-
 	err := createTable(session, `CREATE TYPE gocql_test.scan_null_udt_position(
 		lat int,
 		lon int,