From 9dc896f4ea51276de4ea76ffca3fd719e0c8b8a1 Mon Sep 17 00:00:00 2001 From: Jeff Jirsa Date: Thu, 27 Jul 2017 11:40:52 -0700 Subject: [PATCH] Invalid writetime for null columns in cqlsh Patch by Jeff Jirsa; Reviewed by Aleksey Yeschenko for CASSANDRA-13711 --- CHANGES.txt | 1 + .../cassandra/cql3/selection/Selection.java | 6 ++++ .../validation/entities/TimestampTest.java | 29 +++++++++++++++++++ .../validation/operations/SelectTest.java | 27 +++++++++++++++++ 4 files changed, 63 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 5e95b75868..e3f5fe61cf 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.0.15 + * Fix invalid writetime for null cells (CASSANDRA-13711) * Fix ALTER TABLE statement to atomically propagate changes to the table and its MVs (CASSANDRA-12952) * Fixed ambiguous output of nodetool tablestats command (CASSANDRA-13722) * JMXEnabledThreadPoolExecutor with corePoolSize equal to maxPoolSize (Backport CASSANDRA-13329) diff --git a/src/java/org/apache/cassandra/cql3/selection/Selection.java b/src/java/org/apache/cassandra/cql3/selection/Selection.java index 0ecf06318c..425ae52b2d 100644 --- a/src/java/org/apache/cassandra/cql3/selection/Selection.java +++ b/src/java/org/apache/cassandra/cql3/selection/Selection.java @@ -346,6 +346,12 @@ public abstract class Selection } } current = new ArrayList<>(columns.size()); + + // Timestamps and TTLs are arrays per row, we must null them out between row + if (timestamps != null) + Arrays.fill(timestamps, Long.MIN_VALUE); + if (ttls != null) + Arrays.fill(ttls, -1); } public ResultSet build(int protocolVersion) throws InvalidRequestException diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/TimestampTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/TimestampTest.java index 3e70cd06e5..b41163c424 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/TimestampTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/TimestampTest.java @@ -152,4 +152,33 @@ public class TimestampTest extends CQLTester execute("INSERT INTO %s (k, i) VALUES (1, 1) USING TIMESTAMP ?", unset()); // treat as 'now' } + @Test + public void testTimestampsOnUnsetColumns() throws Throwable + { + createTable("CREATE TABLE %s (k int PRIMARY KEY, i int)"); + execute("INSERT INTO %s (k, i) VALUES (1, 1) USING TIMESTAMP 1;"); + execute("INSERT INTO %s (k) VALUES (2) USING TIMESTAMP 2;"); + execute("INSERT INTO %s (k, i) VALUES (3, 3) USING TIMESTAMP 1;"); + assertRows(execute("SELECT k, i, writetime(i) FROM %s "), + row(1, 1, 1L), + row(2, null, null), + row(3, 3, 1L)); + } + + @Test + public void testTimestampsOnUnsetColumnsWide() throws Throwable + { + createTable("CREATE TABLE %s (k int , c int, i int, PRIMARY KEY (k, c))"); + execute("INSERT INTO %s (k, c, i) VALUES (1, 1, 1) USING TIMESTAMP 1;"); + execute("INSERT INTO %s (k, c) VALUES (1, 2) USING TIMESTAMP 1;"); + execute("INSERT INTO %s (k, c, i) VALUES (1, 3, 1) USING TIMESTAMP 1;"); + execute("INSERT INTO %s (k, c) VALUES (2, 2) USING TIMESTAMP 2;"); + execute("INSERT INTO %s (k, c, i) VALUES (3, 3, 3) USING TIMESTAMP 1;"); + assertRows(execute("SELECT k, c, i, writetime(i) FROM %s "), + row(1, 1, 1, 1L), + row(1, 2, null, null), + row(1, 3, 1, 1L), + row(2, 2, null, null), + row(3, 3, 3, 1L)); + } } diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java index f40ae4ba88..3ee20a0f2d 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java @@ -2966,4 +2966,31 @@ public class SelectTest extends CQLTester }); } + @Test + public void testMixedTTLOnColumns() throws Throwable + { + createTable("CREATE TABLE %s (k int PRIMARY KEY, i int)"); + execute("INSERT INTO %s (k) VALUES (2);"); + execute("INSERT INTO %s (k, i) VALUES (1, 1) USING TTL 100;"); + execute("INSERT INTO %s (k, i) VALUES (3, 3) USING TTL 100;"); + assertRows(execute("SELECT k, i, TTL(i) FROM %s "), + row(1, 1, 100), row(2, null, null), row(3, 3, 100)); + } + + @Test + public void testMixedTTLOnColumnsWide() throws Throwable + { + createTable("CREATE TABLE %s (k int, c int, i int, PRIMARY KEY (k, c))"); + execute("INSERT INTO %s (k, c) VALUES (2, 2);"); + execute("INSERT INTO %s (k, c, i) VALUES (1, 1, 1) USING TTL 100;"); + execute("INSERT INTO %s (k, c) VALUES (1, 2) ;"); + execute("INSERT INTO %s (k, c, i) VALUES (1, 3, 3) USING TTL 100;"); + execute("INSERT INTO %s (k, c, i) VALUES (3, 3, 3) USING TTL 100;"); + assertRows(execute("SELECT k, c, i, TTL(i) FROM %s "), + row(1, 1, 1, 100), + row(1, 2, null, null), + row(1, 3, 3, 100), + row(2, 2, null, null), + row(3, 3, 3, 100)); + } }