diff --git a/CHANGES.txt b/CHANGES.txt index 5be7e1ff40..9c595fccfc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,7 +4,7 @@ * Duplicate the buffer before passing it to analyser in SASI operation (CASSANDRA-13512) * Properly evict pstmts from prepared statements cache (CASSANDRA-13641) Merged from 3.0: -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) * Fix Digest mismatch Exception if hints file has UnknownColumnFamily (CASSANDRA-13696) diff --git a/src/java/org/apache/cassandra/cql3/selection/Selection.java b/src/java/org/apache/cassandra/cql3/selection/Selection.java index 4ea1071789..428634c177 100644 --- a/src/java/org/apache/cassandra/cql3/selection/Selection.java +++ b/src/java/org/apache/cassandra/cql3/selection/Selection.java @@ -404,6 +404,12 @@ public abstract class Selection } } current = new ArrayList<>(columns.size()); + + // Timestamps and TTLs are arrays per row, we must null them out between rows + if (timestamps != null) + Arrays.fill(timestamps, Long.MIN_VALUE); + if (ttls != null) + Arrays.fill(ttls, -1); } /** 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 9a24bd0d7d..7c6c589798 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java @@ -4738,4 +4738,32 @@ public class SelectTest extends CQLTester assertRows(execute("SELECT k, v FROM %s WHERE k = 0 AND m CONTAINS KEY 'c' ALLOW FILTERING"), row(0, 2)); }); } + + @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)); + } }