Skip to content

Commit a12116a

Browse files
authored
JAVA-1397: Handle duration as native datatype in protocol v5+ (apache#801)
1 parent 6aa2792 commit a12116a

11 files changed

Lines changed: 64 additions & 93 deletions

File tree

build.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ java:
3636
os:
3737
- ubuntu/trusty64
3838
cassandra:
39-
- 1.2
40-
- 2.0
41-
- 2.1
42-
- 2.2
43-
- 3.0
44-
- 3.9
39+
- '1.2'
40+
- '2.0'
41+
- '2.1'
42+
- '2.2'
43+
- '3.0'
44+
- '3.10'
4545
build:
4646
- type: maven
4747
version: 3.2.5

changelog/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- [new feature] JAVA-1248: Implement "beta" flag for native protocol v5.
77
- [new feature] JAVA-1362: Send query options flags as [int] for Protocol V5+.
88
- [improvement] JAVA-1367: Make protocol negotiation more resilient.
9+
- [bug] JAVA-1397: Handle duration as native datatype in protocol v5+.
910

1011
Merged from 3.1.x branch:
1112

driver-core/src/main/java/com/datastax/driver/core/DataType.java

Lines changed: 21 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@
1717

1818
import com.datastax.driver.core.exceptions.DriverInternalError;
1919
import com.google.common.base.Objects;
20-
import com.google.common.base.Predicate;
2120
import com.google.common.collect.ImmutableList;
2221
import com.google.common.collect.ImmutableSet;
23-
import com.google.common.collect.Sets;
2422
import io.netty.buffer.ByteBuf;
2523

2624
import java.util.*;
@@ -35,6 +33,7 @@ public abstract class DataType {
3533
*/
3634
public enum Name {
3735

36+
CUSTOM(0),
3837
ASCII(1),
3938
BIGINT(2),
4039
BLOB(3),
@@ -43,7 +42,6 @@ public enum Name {
4342
DECIMAL(6),
4443
DOUBLE(7),
4544
FLOAT(8),
46-
INET(16),
4745
INT(9),
4846
TEXT(10) {
4947
@Override
@@ -61,16 +59,17 @@ public boolean isCompatibleWith(Name that) {
6159
},
6260
VARINT(14),
6361
TIMEUUID(15),
62+
INET(16),
63+
DATE(17, ProtocolVersion.V4),
64+
TIME(18, ProtocolVersion.V4),
65+
SMALLINT(19, ProtocolVersion.V4),
66+
TINYINT(20, ProtocolVersion.V4),
67+
DURATION(21, ProtocolVersion.V5),
6468
LIST(32),
65-
SET(34),
6669
MAP(33),
67-
CUSTOM(0),
70+
SET(34),
6871
UDT(48, ProtocolVersion.V3),
69-
TUPLE(49, ProtocolVersion.V3),
70-
SMALLINT(19, ProtocolVersion.V4),
71-
TINYINT(20, ProtocolVersion.V4),
72-
DATE(17, ProtocolVersion.V4),
73-
TIME(18, ProtocolVersion.V4);
72+
TUPLE(49, ProtocolVersion.V3);
7473

7574
final int protocolId;
7675

@@ -147,6 +146,7 @@ public String toString() {
147146
primitiveTypeMap.put(Name.TINYINT, new DataType.NativeType(Name.TINYINT));
148147
primitiveTypeMap.put(Name.DATE, new DataType.NativeType(Name.DATE));
149148
primitiveTypeMap.put(Name.TIME, new DataType.NativeType(Name.TIME));
149+
primitiveTypeMap.put(Name.DURATION, new DataType.NativeType(Name.DURATION));
150150
}
151151

152152
private static final Set<DataType> primitiveTypeSet = ImmutableSet.copyOf(primitiveTypeMap.values());
@@ -162,9 +162,14 @@ static DataType decode(ByteBuf buffer, ProtocolVersion protocolVersion, CodecReg
162162
switch (name) {
163163
case CUSTOM:
164164
String className = CBUtil.readString(buffer);
165-
return DataTypeClassNameParser.isUserType(className) || DataTypeClassNameParser.isTupleType(className)
166-
? DataTypeClassNameParser.parseOne(className, protocolVersion, codecRegistry)
167-
: custom(className);
165+
if (DataTypeClassNameParser.isDuration(className)) {
166+
return DataType.duration();
167+
} else if (DataTypeClassNameParser.isUserType(className) ||
168+
DataTypeClassNameParser.isTupleType(className)) {
169+
return DataTypeClassNameParser.parseOne(className, protocolVersion, codecRegistry);
170+
} else {
171+
return custom(className);
172+
}
168173
case LIST:
169174
return list(decode(buffer, protocolVersion, codecRegistry));
170175
case SET:
@@ -499,9 +504,7 @@ public static CollectionType frozenMap(DataType keyType, DataType valueType) {
499504
public static DataType.CustomType custom(String typeClassName) {
500505
if (typeClassName == null)
501506
throw new NullPointerException();
502-
return DataTypeClassNameParser.isDuration(typeClassName)
503-
? DataType.duration()
504-
: new DataType.CustomType(Name.CUSTOM, typeClassName);
507+
return new DataType.CustomType(Name.CUSTOM, typeClassName);
505508
}
506509

507510
/**
@@ -513,8 +516,8 @@ public static DataType.CustomType custom(String typeClassName) {
513516
*
514517
* @return the Duration type. The returned instance is a singleton.
515518
*/
516-
public static DurationType duration() {
517-
return DurationType.instance;
519+
public static DataType duration() {
520+
return primitiveTypeMap.get(Name.DURATION);
518521
}
519522

520523
/**
@@ -578,26 +581,6 @@ public static Set<DataType> allPrimitiveTypes() {
578581
return primitiveTypeSet;
579582
}
580583

581-
/**
582-
* Returns a set of all primitive types supported by the given protocolVersion.
583-
* <p>
584-
* Primitive types are defined as the types that don't have type arguments
585-
* (that is excluding lists, sets, and maps, tuples and udts).
586-
* </p>
587-
*
588-
* @param protocolVersion protocol version to get types for.
589-
* @return returns a set of all the primitive types for the given protocolVersion.
590-
*/
591-
static Set<DataType> allPrimitiveTypes(final ProtocolVersion protocolVersion) {
592-
return Sets.filter(primitiveTypeSet, new Predicate<DataType>() {
593-
594-
@Override
595-
public boolean apply(DataType dataType) {
596-
return protocolVersion.compareTo(dataType.getName().minProtocolVersion) >= 0;
597-
}
598-
});
599-
}
600-
601584
/**
602585
* Returns a String representation of this data type
603586
* suitable for inclusion as a parameter type
@@ -775,24 +758,4 @@ public String toString() {
775758
}
776759
}
777760

778-
/**
779-
* The Duration type, introduced in Cassandra 3.10.
780-
* <p/>
781-
* This class is a singleton; to obtain its unique instance,
782-
* call {@link #duration()}.
783-
*/
784-
public static class DurationType extends CustomType {
785-
786-
private static final DurationType instance = new DurationType();
787-
788-
private DurationType() {
789-
super(Name.CUSTOM, "org.apache.cassandra.db.marshal.DurationType");
790-
}
791-
792-
@Override
793-
public String toString() {
794-
return "duration";
795-
}
796-
}
797-
798761
}

driver-core/src/test/java/com/datastax/driver/core/DataTypeIntegrationTest.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ private static List<TestTable> tablesWithPrimitivesNull() {
212212
List<TestTable> tables = Lists.newArrayList();
213213
// Create a test table for each primitive type testing with null values. If the
214214
// type maps to a java primitive type it's value will by the default value instead of null.
215-
for (DataType dataType : DataType.allPrimitiveTypes(TestUtils.getDesiredProtocolVersion())) {
215+
for (DataType dataType : TestUtils.allPrimitiveTypes(TestUtils.getDesiredProtocolVersion())) {
216216
Object expectedPrimitiveValue = null;
217217
switch (dataType.getName()) {
218218
case BIGINT:
@@ -237,11 +237,14 @@ private static List<TestTable> tablesWithPrimitivesNull() {
237237
case BOOLEAN:
238238
expectedPrimitiveValue = false;
239239
break;
240+
case COUNTER:
241+
case DURATION:
242+
// Duration is handled separately in DurationIntegrationTest, because it has specific restrictions (e.g.
243+
// not allowed in collections).
244+
continue;
240245
}
241246

242-
if (!dataType.getName().equals(DataType.Name.COUNTER)) {
243-
tables.add(new TestTable(dataType, null, null, expectedPrimitiveValue, "1.2.0"));
244-
}
247+
tables.add(new TestTable(dataType, null, null, expectedPrimitiveValue, "1.2.0"));
245248
}
246249
return tables;
247250

driver-core/src/test/java/com/datastax/driver/core/DataTypeTest.java

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class DataTypeTest {
4040
ProtocolVersion protocolVersion = TestUtils.getDesiredProtocolVersion();
4141

4242
static boolean exclude(DataType t) {
43-
return t.getName() == DataType.Name.COUNTER;
43+
return t.getName() == DataType.Name.COUNTER || t.getName() == DataType.Name.DURATION;
4444
}
4545

4646
/**
@@ -317,22 +317,4 @@ public void serializeDeserializeCollectionsTest(ProtocolVersion version) {
317317
fail("This should not have worked");
318318
} catch (InvalidTypeException e) { /* That's what we want */ }
319319
}
320-
321-
@Test(groups = "unit")
322-
public void should_not_return_v4_types_in_all_primitive_types_with_v3() {
323-
Set<DataType> dataTypes = DataType.allPrimitiveTypes(ProtocolVersion.V3);
324-
325-
// Ensure it does not contain specific newer types tinyint and smallint.
326-
assertThat(dataTypes).doesNotContainAnyElementsOf(newArrayList(DataType.tinyint(), DataType.smallint()));
327-
328-
// Ensure all values are <= V3.
329-
for (DataType dataType : dataTypes) {
330-
assertThat(dataType.getName().minProtocolVersion).isLessThanOrEqualTo(ProtocolVersion.V3);
331-
}
332-
}
333-
334-
@Test(groups = "unit")
335-
public void should_return_same_elements_with_all_primitive_types_using_latest_protocol_version() {
336-
assertThat(DataType.allPrimitiveTypes(ProtocolVersion.NEWEST_SUPPORTED)).isEqualTo(DataType.allPrimitiveTypes());
337-
}
338320
}

driver-core/src/test/java/com/datastax/driver/core/PreparedStatementTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ public class PreparedStatementTest extends CCMTestsSupport {
4848
private static final String SIMPLE_TABLE = "test";
4949
private static final String SIMPLE_TABLE2 = "test2";
5050

51-
private final Collection<DataType> primitiveTypes = DataType.allPrimitiveTypes(TestUtils.getDesiredProtocolVersion());
51+
private final Collection<DataType> primitiveTypes = allPrimitiveTypes(TestUtils.getDesiredProtocolVersion());
5252

5353
private boolean exclude(DataType t) {
54-
return t.getName() == DataType.Name.COUNTER;
54+
// duration is not supported in collections
55+
return t.getName() == DataType.Name.COUNTER || t.getName() == DataType.Name.DURATION;
5556
}
5657

5758
@Override

driver-core/src/test/java/com/datastax/driver/core/PrimitiveTypeSamples.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class PrimitiveTypeSamples {
4040

4141
private static Map<DataType, Object> generateAll() {
4242
try {
43-
final Collection<DataType> primitiveTypes = DataType.allPrimitiveTypes(TestUtils.getDesiredProtocolVersion());
43+
final Collection<DataType> primitiveTypes = TestUtils.allPrimitiveTypes(TestUtils.getDesiredProtocolVersion());
4444
ImmutableMap<DataType, Object> data = ImmutableMap.<DataType, Object>builder()
4545
.put(DataType.ascii(), "ascii")
4646
.put(DataType.bigint(), Long.MAX_VALUE)

driver-core/src/test/java/com/datastax/driver/core/QueryLoggerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
public class QueryLoggerTest extends CCMTestsSupport {
5151

5252
private static final List<DataType> dataTypes = new ArrayList<DataType>(
53-
Sets.filter(DataType.allPrimitiveTypes(TestUtils.getDesiredProtocolVersion()), new Predicate<DataType>() {
53+
Sets.filter(TestUtils.allPrimitiveTypes(TestUtils.getDesiredProtocolVersion()), new Predicate<DataType>() {
5454
@Override
5555
public boolean apply(DataType type) {
5656
return type != DataType.counter();

driver-core/src/test/java/com/datastax/driver/core/TestUtils.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.datastax.driver.core.policies.WhiteListPolicy;
2020
import com.google.common.base.Predicate;
2121
import com.google.common.base.Throwables;
22+
import com.google.common.collect.Sets;
2223
import com.google.common.util.concurrent.Futures;
2324
import com.google.common.util.concurrent.Uninterruptibles;
2425
import com.sun.management.OperatingSystemMXBean;
@@ -463,6 +464,26 @@ public static Object getFixedValue2(final DataType type) {
463464
throw new RuntimeException("Missing handling of " + type);
464465
}
465466

467+
/**
468+
* Returns a set of all primitive types supported by the given protocolVersion.
469+
* <p>
470+
* Primitive types are defined as the types that don't have type arguments
471+
* (that is excluding lists, sets, and maps, tuples and udts).
472+
* </p>
473+
*
474+
* @param protocolVersion protocol version to get types for.
475+
* @return returns a set of all the primitive types for the given protocolVersion.
476+
*/
477+
static Set<DataType> allPrimitiveTypes(final ProtocolVersion protocolVersion) {
478+
return Sets.filter(DataType.allPrimitiveTypes(), new Predicate<DataType>() {
479+
480+
@Override
481+
public boolean apply(DataType dataType) {
482+
return protocolVersion.compareTo(dataType.getName().minProtocolVersion) >= 0;
483+
}
484+
});
485+
}
486+
466487
// Wait for a node to be up and running
467488
// This is used because there is some delay between when a node has been
468489
// added through ccm and when it's actually available for querying

driver-core/src/test/java/com/datastax/driver/core/UserTypesTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
@CassandraVersion("2.1.0")
4040
public class UserTypesTest extends CCMTestsSupport {
4141

42-
private final static List<DataType> DATA_TYPE_PRIMITIVES = new ArrayList<DataType>(DataType.allPrimitiveTypes(TestUtils.getDesiredProtocolVersion()));
42+
private final static List<DataType> DATA_TYPE_PRIMITIVES = new ArrayList<DataType>(TestUtils.allPrimitiveTypes(TestUtils.getDesiredProtocolVersion()));
4343

4444
static {
4545
DATA_TYPE_PRIMITIVES.remove(DataType.counter());

0 commit comments

Comments
 (0)