This is an automated email from the ASF dual-hosted git repository. samt pushed a commit to branch cassandra-4.0 in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/cassandra-4.0 by this push: new c961310 Backward compatibility for CQLSSTableWriter Date fields c961310 is described below commit c96131035b309dcc8d716fb0a57ff9d46a8c5042 Author: Doug Rohrer <droh...@apple.com> AuthorDate: Wed Nov 3 15:50:54 2021 -0500 Backward compatibility for CQLSSTableWriter Date fields Patch by Doug Rohrer; reviewed by Brandon Williams and Sam Tunnicliffe for CASSANDRA-17117 --- CHANGES.txt | 1 + .../apache/cassandra/cql3/UntypedResultSet.java | 4 ++ .../cassandra/cql3/functions/types/LocalDate.java | 6 +-- .../cassandra/io/sstable/CQLSSTableWriter.java | 20 +++++++--- .../cassandra/io/sstable/CQLSSTableWriterTest.java | 43 ++++++++++++++++++++++ 5 files changed, 66 insertions(+), 8 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 2093f0c..3f6bacd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 4.0.2 + * Add backward compatibility for CQLSSTableWriter Date fields (CASSANDRA-17117) * Push initial client connection messages to trace (CASSANDRA-17038) * Correct the internode message timestamp if sending node has wrapped (CASSANDRA-16997) * Avoid race causing us to return null in RangesAtEndpoint (CASSANDRA-16965) diff --git a/src/java/org/apache/cassandra/cql3/UntypedResultSet.java b/src/java/org/apache/cassandra/cql3/UntypedResultSet.java index f4ac99f..169ec8d 100644 --- a/src/java/org/apache/cassandra/cql3/UntypedResultSet.java +++ b/src/java/org/apache/cassandra/cql3/UntypedResultSet.java @@ -24,6 +24,8 @@ import java.util.*; import com.google.common.annotations.VisibleForTesting; +import com.datastax.driver.core.CodecUtils; +import org.apache.cassandra.cql3.functions.types.LocalDate; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.cql3.statements.SelectStatement; import org.apache.cassandra.db.*; @@ -394,6 +396,8 @@ public abstract class UntypedResultSet implements Iterable<UntypedResultSet.Row> return TimestampType.instance.compose(data.get(column)); } + public LocalDate getDate(String column) { return LocalDate.fromDaysSinceEpoch(CodecUtils.fromUnsignedToSignedInt(data.get(column).getInt()));} + public long getLong(String column) { return LongType.instance.compose(data.get(column)); diff --git a/src/java/org/apache/cassandra/cql3/functions/types/LocalDate.java b/src/java/org/apache/cassandra/cql3/functions/types/LocalDate.java index dead6ec..a8b4236 100644 --- a/src/java/org/apache/cassandra/cql3/functions/types/LocalDate.java +++ b/src/java/org/apache/cassandra/cql3/functions/types/LocalDate.java @@ -62,7 +62,7 @@ public final class LocalDate * @param daysSinceEpoch the number of days. * @return the new instance. */ - static LocalDate fromDaysSinceEpoch(int daysSinceEpoch) + public static LocalDate fromDaysSinceEpoch(int daysSinceEpoch) { return new LocalDate(daysSinceEpoch); } @@ -76,7 +76,7 @@ public final class LocalDate * @throws IllegalArgumentException if the date is not in the range [-5877641-06-23; * 5881580-07-11]. */ - static LocalDate fromMillisSinceEpoch(long millisSinceEpoch) + public static LocalDate fromMillisSinceEpoch(long millisSinceEpoch) throws IllegalArgumentException { long daysSinceEpoch = TimeUnit.MILLISECONDS.toDays(millisSinceEpoch); @@ -92,7 +92,7 @@ public final class LocalDate * * @return the number of days. */ - int getDaysSinceEpoch() + public int getDaysSinceEpoch() { return daysSinceEpoch; } diff --git a/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java b/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java index 8ac0fdf..0b8dbae 100644 --- a/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java +++ b/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java @@ -44,6 +44,7 @@ import org.apache.cassandra.cql3.statements.ModificationStatement; import org.apache.cassandra.cql3.statements.UpdateStatement; import org.apache.cassandra.db.Clustering; import org.apache.cassandra.db.SystemKeyspace; +import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.dht.Murmur3Partitioner; import org.apache.cassandra.exceptions.InvalidRequestException; @@ -167,7 +168,7 @@ public class CQLSSTableWriter implements Closeable for (int i = 0; i < size; i++) { Object value = values.get(i); - rawValues.add(serialize(value, typeCodecs.get(i))); + rawValues.add(serialize(value, typeCodecs.get(i), boundNames.get(i))); } return rawAddRow(rawValues); @@ -202,7 +203,7 @@ public class CQLSSTableWriter implements Closeable { ColumnSpecification spec = boundNames.get(i); Object value = values.get(spec.name.toString()); - rawValues.add(serialize(value, typeCodecs.get(i))); + rawValues.add(serialize(value, typeCodecs.get(i), boundNames.get(i))); } return rawAddRow(rawValues); } @@ -287,7 +288,7 @@ public class CQLSSTableWriter implements Closeable { int size = Math.min(values.size(), boundNames.size()); List<ByteBuffer> rawValues = new ArrayList<>(size); - for (int i = 0; i < size; i++) + for (int i = 0; i < size; i++) { ColumnSpecification spec = boundNames.get(i); rawValues.add(values.get(spec.name.toString())); @@ -320,12 +321,21 @@ public class CQLSSTableWriter implements Closeable writer.close(); } - private ByteBuffer serialize(Object value, TypeCodec codec) + private ByteBuffer serialize(Object value, TypeCodec codec, ColumnSpecification columnSpecification) { if (value == null || value == UNSET_VALUE) return (ByteBuffer) value; - return codec.serialize(value, ProtocolVersion.CURRENT); + try + { + return codec.serialize(value, ProtocolVersion.CURRENT); + } + catch (ClassCastException cce) + { + // For backwards-compatibility with consumers that may be passing + // an Integer for a Date field, for example. + return ((AbstractType)columnSpecification.type).decompose(value); + } } /** * A Builder for a CQLSSTableWriter object. diff --git a/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java b/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java index 31c588b..dd7085a 100644 --- a/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java +++ b/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java @@ -24,6 +24,9 @@ import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -43,10 +46,12 @@ import org.apache.cassandra.cql3.functions.UDHelper; import org.apache.cassandra.cql3.functions.types.*; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.commitlog.CommitLog; +import org.apache.cassandra.db.rows.Row; import org.apache.cassandra.dht.*; import org.apache.cassandra.exceptions.*; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.schema.TableMetadataRef; +import org.apache.cassandra.serializers.SimpleDateSerializer; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.*; @@ -634,6 +639,44 @@ public class CQLSSTableWriterTest assertEquals(100, resultSet.size()); } + @Test + public void testDateType() throws Exception + { + // Test to make sure we can write to `date` fields in both old and new formats + String schema = "CREATE TABLE " + qualifiedTable + " (" + + " k int," + + " c date," + + " PRIMARY KEY (k)" + + ")"; + String insert = "INSERT INTO " + qualifiedTable + " (k, c) VALUES (?, ?)"; + CQLSSTableWriter writer = CQLSSTableWriter.builder() + .inDirectory(dataDir) + .forTable(schema) + .using(insert) + .withBufferSizeInMB(1) + .build(); + + final int ID_OFFSET = 1000; + for (int i = 0; i < 100 ; i++) { + // Use old-style integer as date to test backwards-compatibility + writer.addRow(i, i - Integer.MIN_VALUE); // old-style raw integer needs to be offset + // Use new-style `LocalDate` for date value. + writer.addRow(i + ID_OFFSET, LocalDate.fromDaysSinceEpoch(i)); + } + writer.close(); + loadSSTables(dataDir, keyspace); + + UntypedResultSet rs = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + ";"); + assertEquals(200, rs.size()); + Map<Integer, LocalDate> map = StreamSupport.stream(rs.spliterator(), false) + .collect(Collectors.toMap( r -> r.getInt("k"), r -> r.getDate("c"))); + for (int i = 0; i < 100; i++) { + final LocalDate expected = LocalDate.fromDaysSinceEpoch(i); + assertEquals(expected, map.get(i + ID_OFFSET)); + assertEquals(expected, map.get(i)); + } + } + private static void loadSSTables(File dataDir, String ks) throws ExecutionException, InterruptedException { SSTableLoader loader = new SSTableLoader(dataDir, new SSTableLoader.Client() --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org