You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by jm...@apache.org on 2015/05/14 22:35:17 UTC
cassandra git commit: Follow-up for 7523: prevent old clients from
getting new type codes.
Repository: cassandra
Updated Branches:
refs/heads/trunk b2abcb7fc -> 4a5c282f7
Follow-up for 7523: prevent old clients from getting new type codes.
Patch by jmckenzie; reviewed by Aleksey Yeschenko for CASSANDRA-9219
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/4a5c282f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/4a5c282f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/4a5c282f
Branch: refs/heads/trunk
Commit: 4a5c282f7615cc97929d76f36fe82e190fecbb89
Parents: b2abcb7
Author: Josh McKenzie <jo...@datastax.com>
Authored: Thu May 14 15:33:29 2015 -0500
Committer: Josh McKenzie <jo...@datastax.com>
Committed: Thu May 14 15:33:29 2015 -0500
----------------------------------------------------------------------
.../cassandra/db/marshal/SimpleDateType.java | 2 +-
.../apache/cassandra/db/marshal/TimeType.java | 3 +-
.../apache/cassandra/transport/DataType.java | 82 +++++++++-----
.../apache/cassandra/transport/OptionCodec.java | 14 +--
.../cassandra/transport/DataTypeTest.java | 108 +++++++++++++++++++
5 files changed, 172 insertions(+), 37 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4a5c282f/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java b/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java
index a34646f..225b9cc 100644
--- a/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java
+++ b/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java
@@ -39,6 +39,7 @@ public class SimpleDateType extends AbstractType<Integer>
return ByteBufferUtil.compareUnsigned(o1, o2);
}
+ @Override
public boolean isByteOrderComparable()
{
return true;
@@ -61,7 +62,6 @@ public class SimpleDateType extends AbstractType<Integer>
return this == otherType || otherType == IntegerType.instance;
}
- @Override
public Term fromJSONObject(Object parsed) throws MarshalException
{
try
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4a5c282f/src/java/org/apache/cassandra/db/marshal/TimeType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/TimeType.java b/src/java/org/apache/cassandra/db/marshal/TimeType.java
index c5c7b98..c241a38 100644
--- a/src/java/org/apache/cassandra/db/marshal/TimeType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TimeType.java
@@ -45,6 +45,7 @@ public class TimeType extends AbstractType<Long>
return decompose(TimeSerializer.timeStringToLong(source));
}
+ @Override
public boolean isByteOrderComparable()
{
return true;
@@ -62,7 +63,6 @@ public class TimeType extends AbstractType<Long>
return this == otherType || otherType == LongType.instance;
}
- @Override
public Term fromJSONObject(Object parsed) throws MarshalException
{
try
@@ -82,6 +82,7 @@ public class TimeType extends AbstractType<Long>
return '"' + TimeSerializer.instance.toString(TimeSerializer.instance.deserialize(buffer)) + '"';
}
+ @Override
public CQL3Type asCQL3Type()
{
return CQL3Type.Native.TIME;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4a5c282f/src/java/org/apache/cassandra/transport/DataType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/transport/DataType.java b/src/java/org/apache/cassandra/transport/DataType.java
index e13194d..a78b740 100644
--- a/src/java/org/apache/cassandra/transport/DataType.java
+++ b/src/java/org/apache/cassandra/transport/DataType.java
@@ -24,6 +24,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.List;
+import com.google.common.annotations.VisibleForTesting;
+
import io.netty.buffer.ByteBuf;
import org.apache.cassandra.exceptions.RequestValidationException;
@@ -32,35 +34,36 @@ import org.apache.cassandra.utils.Pair;
public enum DataType implements OptionCodec.Codecable<DataType>
{
- CUSTOM (0, null),
- ASCII (1, AsciiType.instance),
- BIGINT (2, LongType.instance),
- BLOB (3, BytesType.instance),
- BOOLEAN (4, BooleanType.instance),
- COUNTER (5, CounterColumnType.instance),
- DECIMAL (6, DecimalType.instance),
- DOUBLE (7, DoubleType.instance),
- FLOAT (8, FloatType.instance),
- INT (9, Int32Type.instance),
- TEXT (10, UTF8Type.instance),
- TIMESTAMP(11, TimestampType.instance),
- UUID (12, UUIDType.instance),
- VARCHAR (13, UTF8Type.instance),
- VARINT (14, IntegerType.instance),
- TIMEUUID (15, TimeUUIDType.instance),
- INET (16, InetAddressType.instance),
- DATE (17, DateType.instance),
- TIME (18, TimeType.instance),
- LIST (32, null),
- MAP (33, null),
- SET (34, null),
- UDT (48, null),
- TUPLE (49, null);
+ CUSTOM (0, null, 1),
+ ASCII (1, AsciiType.instance, 1),
+ BIGINT (2, LongType.instance, 1),
+ BLOB (3, BytesType.instance, 1),
+ BOOLEAN (4, BooleanType.instance, 1),
+ COUNTER (5, CounterColumnType.instance, 1),
+ DECIMAL (6, DecimalType.instance, 1),
+ DOUBLE (7, DoubleType.instance, 1),
+ FLOAT (8, FloatType.instance, 1),
+ INT (9, Int32Type.instance, 1),
+ TEXT (10, UTF8Type.instance, 1),
+ TIMESTAMP(11, TimestampType.instance, 1),
+ UUID (12, UUIDType.instance, 1),
+ VARCHAR (13, UTF8Type.instance, 1),
+ VARINT (14, IntegerType.instance, 1),
+ TIMEUUID (15, TimeUUIDType.instance, 1),
+ INET (16, InetAddressType.instance, 1),
+ DATE (17, SimpleDateType.instance, 4),
+ TIME (18, TimeType.instance, 4),
+ LIST (32, null, 1),
+ MAP (33, null, 1),
+ SET (34, null, 1),
+ UDT (48, null, 3),
+ TUPLE (49, null, 3);
public static final OptionCodec<DataType> codec = new OptionCodec<DataType>(DataType.class);
private final int id;
+ private final int protocolVersion;
private final AbstractType type;
private static final Map<AbstractType, DataType> dataTypeMap = new HashMap<AbstractType, DataType>();
static
@@ -72,14 +75,17 @@ public enum DataType implements OptionCodec.Codecable<DataType>
}
}
- private DataType(int id, AbstractType type)
+ DataType(int id, AbstractType type, int protocolVersion)
{
this.id = id;
this.type = type;
+ this.protocolVersion = protocolVersion;
}
- public int getId()
+ public int getId(int version)
{
+ if (version < protocolVersion)
+ return DataType.CUSTOM.getId(version);
return id;
}
@@ -123,6 +129,13 @@ public enum DataType implements OptionCodec.Codecable<DataType>
public void writeValue(Object value, ByteBuf cb, int version)
{
+ // Serialize as CUSTOM if client on the other side's version is < required for type
+ if (version < protocolVersion)
+ {
+ CBUtil.writeString(value.toString(), cb);
+ return;
+ }
+
switch (this)
{
case CUSTOM:
@@ -162,6 +175,10 @@ public enum DataType implements OptionCodec.Codecable<DataType>
public int serializedValueSize(Object value, int version)
{
+ // Serialize as CUSTOM if client on the other side's version is < required for type
+ if (version < protocolVersion)
+ return CBUtil.sizeOfString(value.toString());
+
switch (this)
{
case CUSTOM:
@@ -230,16 +247,19 @@ public enum DataType implements OptionCodec.Codecable<DataType>
throw new AssertionError();
}
- if (type instanceof UserType && version >= 3)
+ if (type instanceof UserType && version >= UDT.protocolVersion)
return Pair.<DataType, Object>create(UDT, type);
- if (type instanceof TupleType && version >= 3)
+ if (type instanceof TupleType && version >= TUPLE.protocolVersion)
return Pair.<DataType, Object>create(TUPLE, type);
return Pair.<DataType, Object>create(CUSTOM, type.toString());
}
else
{
+ // Fall back to CUSTOM if target doesn't know this data type
+ if (version < dt.protocolVersion)
+ return Pair.<DataType, Object>create(CUSTOM, type.toString());
return Pair.create(dt, null);
}
}
@@ -272,4 +292,10 @@ public enum DataType implements OptionCodec.Codecable<DataType>
throw new ProtocolException(e.getMessage());
}
}
+
+ @VisibleForTesting
+ public int getProtocolVersion()
+ {
+ return protocolVersion;
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4a5c282f/src/java/org/apache/cassandra/transport/OptionCodec.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/transport/OptionCodec.java b/src/java/org/apache/cassandra/transport/OptionCodec.java
index ec2a1fa..3a8b813 100644
--- a/src/java/org/apache/cassandra/transport/OptionCodec.java
+++ b/src/java/org/apache/cassandra/transport/OptionCodec.java
@@ -30,7 +30,7 @@ public class OptionCodec<T extends Enum<T> & OptionCodec.Codecable<T>>
{
public interface Codecable<T extends Enum<T>>
{
- public int getId();
+ public int getId(int version);
public Object readValue(ByteBuf cb, int version);
public void writeValue(Object value, ByteBuf cb, int version);
@@ -48,13 +48,13 @@ public class OptionCodec<T extends Enum<T> & OptionCodec.Codecable<T>>
T[] values = klass.getEnumConstants();
int maxId = -1;
for (T opt : values)
- maxId = Math.max(maxId, opt.getId());
+ maxId = Math.max(maxId, opt.getId(Server.CURRENT_VERSION));
ids = (T[])Array.newInstance(klass, maxId + 1);
for (T opt : values)
{
- if (ids[opt.getId()] != null)
- throw new IllegalStateException(String.format("Duplicate option id %d", opt.getId()));
- ids[opt.getId()] = opt;
+ if (ids[opt.getId(Server.CURRENT_VERSION)] != null)
+ throw new IllegalStateException(String.format("Duplicate option id %d", opt.getId(Server.CURRENT_VERSION)));
+ ids[opt.getId(Server.CURRENT_VERSION)] = opt;
}
}
@@ -91,7 +91,7 @@ public class OptionCodec<T extends Enum<T> & OptionCodec.Codecable<T>>
for (Map.Entry<T, Object> entry : options.entrySet())
{
T opt = entry.getKey();
- cb.writeShort(opt.getId());
+ cb.writeShort(opt.getId(version));
opt.writeValue(entry.getValue(), cb, version);
}
return cb;
@@ -108,7 +108,7 @@ public class OptionCodec<T extends Enum<T> & OptionCodec.Codecable<T>>
{
T opt = option.left;
Object obj = option.right;
- dest.writeShort(opt.getId());
+ dest.writeShort(opt.getId(version));
opt.writeValue(obj, dest, version);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4a5c282f/test/unit/org/apache/cassandra/transport/DataTypeTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/transport/DataTypeTest.java b/test/unit/org/apache/cassandra/transport/DataTypeTest.java
new file mode 100644
index 0000000..dc2c4e2
--- /dev/null
+++ b/test/unit/org/apache/cassandra/transport/DataTypeTest.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.transport;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import io.netty.buffer.ByteBuf;
+import org.apache.cassandra.db.TypeSizes;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.AsciiType;
+import org.apache.cassandra.db.marshal.LongType;
+
+import static org.junit.Assert.assertEquals;
+
+public class DataTypeTest
+{
+ @Test
+ public void TestSimpleDataTypeSerialization()
+ {
+ for (DataType type : DataType.values())
+ {
+ if (isComplexType(type))
+ continue;
+
+ Map<DataType, Object> options = Collections.singletonMap(type, (Object)type.toString());
+ for (int version = 1; version < 5; version++)
+ testEncodeDecode(type, options, version);
+ }
+ }
+
+ @Test
+ public void TestListDataTypeSerialization()
+ {
+ DataType type = DataType.LIST;
+ Map<DataType, Object> options = Collections.singletonMap(type, (Object)LongType.instance);
+ for (int version = 1; version < 5; version++)
+ testEncodeDecode(type, options, version);
+ }
+
+ @Test
+ public void TestMapDataTypeSerialization()
+ {
+ DataType type = DataType.MAP;
+ List<AbstractType> value = new ArrayList<>();
+ value.add(LongType.instance);
+ value.add(AsciiType.instance);
+ Map<DataType, Object> options = Collections.singletonMap(type, (Object)value);
+ for (int version = 1; version < 5; version++)
+ testEncodeDecode(type, options, version);
+ }
+
+ private void testEncodeDecode(DataType type, Map<DataType, Object> options, int version)
+ {
+ ByteBuf dest = type.codec.encode(options, version);
+ Map<DataType, Object> results = type.codec.decode(dest, version);
+
+ for (DataType key : results.keySet())
+ {
+ int ssize = type.serializedValueSize(results.get(key), version);
+ int esize = version < type.getProtocolVersion() ? 2 + TypeSizes.encodedUTF8Length(results.get(key).toString()) : 0;
+ switch (type)
+ {
+ case LIST:
+ case SET:
+ esize += 2;
+ break;
+ case MAP:
+ esize += 4;
+ break;
+ case CUSTOM:
+ esize = 8;
+ break;
+ }
+ assertEquals(esize, ssize);
+
+ DataType expected = version < type.getProtocolVersion()
+ ? DataType.CUSTOM
+ : type;
+ assertEquals(expected, key);
+ }
+ }
+
+ private boolean isComplexType(DataType type)
+ {
+ return type.getId(Server.CURRENT_VERSION) >= 32;
+ }
+}