| /* |
| * 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.cql3.validation.entities; |
| |
| import org.junit.Test; |
| |
| import org.apache.cassandra.cql3.CQLTester; |
| import org.apache.cassandra.cql3.UntypedResultSet; |
| import org.apache.cassandra.exceptions.InvalidRequestException; |
| |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static java.lang.String.format; |
| |
| public class WritetimeOrTTLTest extends CQLTester |
| { |
| private static final long TIMESTAMP_1 = 1; |
| private static final long TIMESTAMP_2 = 2; |
| private static final Long NO_TIMESTAMP = null; |
| |
| private static final int TTL_1 = 10000; |
| private static final int TTL_2 = 20000; |
| private static final Integer NO_TTL = null; |
| |
| @Test |
| public void testSimple() throws Throwable |
| { |
| createTable("CREATE TABLE %s (pk int, ck int, v int, PRIMARY KEY(pk, ck))"); |
| |
| // Primary key columns should be rejected |
| assertInvalidPrimaryKeySelection("pk"); |
| assertInvalidPrimaryKeySelection("ck"); |
| |
| // No rows |
| assertEmpty(execute("SELECT WRITETIME(v) FROM %s")); |
| assertEmpty(execute("SELECT TTL(v) FROM %s")); |
| |
| // Insert row without TTL |
| execute("INSERT INTO %s (pk, ck, v) VALUES (1, 2, 3) USING TIMESTAMP ?", TIMESTAMP_1); |
| assertWritetimeAndTTL("v", TIMESTAMP_1, NO_TTL); |
| |
| // Update the row with TTL and a new timestamp |
| execute("UPDATE %s USING TIMESTAMP ? AND TTL ? SET v=8 WHERE pk=1 AND ck=2", TIMESTAMP_2, TTL_1); |
| assertWritetimeAndTTL("v", TIMESTAMP_2, TTL_1); |
| |
| // Combine with other columns |
| assertRows("SELECT pk, WRITETIME(v) FROM %s", row(1, TIMESTAMP_2)); |
| assertRows("SELECT WRITETIME(v), pk FROM %s", row(TIMESTAMP_2, 1)); |
| assertRows("SELECT pk, WRITETIME(v), v, ck FROM %s", row(1, TIMESTAMP_2, 8, 2)); |
| } |
| |
| @Test |
| public void testList() throws Throwable |
| { |
| createTable("CREATE TABLE %s (k int PRIMARY KEY, l list<int>)"); |
| assertInvalidMultiCellSelection("l"); |
| } |
| |
| @Test |
| public void testFrozenList() throws Throwable |
| { |
| createTable("CREATE TABLE %s (k int PRIMARY KEY, v frozen<list<int>>)"); |
| |
| // Null column |
| execute("INSERT INTO %s (k) VALUES (1) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_1, TTL_1); |
| assertWritetimeAndTTL("v", NO_TIMESTAMP, NO_TTL); |
| |
| // Create empty |
| execute("INSERT INTO %s (k, v) VALUES (1, []) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_1, TTL_1); |
| assertWritetimeAndTTL("v", TIMESTAMP_1, TTL_1); |
| |
| // Update with a single element without TTL |
| execute("INSERT INTO %s (k, v) VALUES (1, [1]) USING TIMESTAMP ?", TIMESTAMP_1); |
| assertWritetimeAndTTL("v", TIMESTAMP_1, NO_TTL); |
| |
| // Add a new element to the list with a new timestamp and a TTL |
| execute("INSERT INTO %s (k, v) VALUES (1, [1, 2, 3]) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_2, TTL_2); |
| assertWritetimeAndTTL("v", TIMESTAMP_2, TTL_2); |
| } |
| |
| @Test |
| public void testSet() throws Throwable |
| { |
| createTable("CREATE TABLE %s (k int PRIMARY KEY, s set<int>)"); |
| assertInvalidMultiCellSelection("s"); |
| } |
| |
| @Test |
| public void testFrozenSet() throws Throwable |
| { |
| createTable("CREATE TABLE %s (k int PRIMARY KEY, s frozen<set<int>>)"); |
| |
| // Null column |
| execute("INSERT INTO %s (k) VALUES (1) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_1, TTL_1); |
| assertWritetimeAndTTL("s", NO_TIMESTAMP, NO_TTL); |
| |
| // Create empty |
| execute("INSERT INTO %s (k, s) VALUES (1, {}) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_1, TTL_1); |
| assertWritetimeAndTTL("s", TIMESTAMP_1, TTL_1); |
| |
| // Update with a single element without TTL |
| execute("INSERT INTO %s (k, s) VALUES (1, {1}) USING TIMESTAMP ?", TIMESTAMP_1); |
| assertWritetimeAndTTL("s", TIMESTAMP_1, NO_TTL); |
| |
| // Add a new element to the set with a new timestamp and a TTL |
| execute("INSERT INTO %s (k, s) VALUES (1, {1, 2}) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_2, TTL_2); |
| assertWritetimeAndTTL("s", TIMESTAMP_2, TTL_2); |
| } |
| |
| @Test |
| public void testMap() throws Throwable |
| { |
| createTable("CREATE TABLE %s (k int PRIMARY KEY, m map<int, int>)"); |
| assertInvalidMultiCellSelection("m"); |
| } |
| |
| @Test |
| public void testFrozenMap() throws Throwable |
| { |
| createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<int,int>>)"); |
| |
| // Null column |
| execute("INSERT INTO %s (k) VALUES (1) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_1, TTL_1); |
| assertWritetimeAndTTL("m", NO_TIMESTAMP, NO_TTL); |
| |
| // Create empty |
| execute("INSERT INTO %s (k, m) VALUES (1, {}) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_1, TTL_1); |
| assertWritetimeAndTTL("m", TIMESTAMP_1, TTL_1); |
| |
| // Create with a single element without TTL |
| execute("INSERT INTO %s (k, m) VALUES (1, {1:10}) USING TIMESTAMP ?", TIMESTAMP_1); |
| assertWritetimeAndTTL("m", TIMESTAMP_1, NO_TTL); |
| |
| // Add a new element to the map with a new timestamp and a TTL |
| execute("INSERT INTO %s (k, m) VALUES (1, {1:10, 2:20}) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_2, TTL_2); |
| assertWritetimeAndTTL("m", TIMESTAMP_2, TTL_2); |
| } |
| |
| @Test |
| public void testFrozenUDT() throws Throwable |
| { |
| String type = createType("CREATE TYPE %s (f1 int, f2 int)"); |
| createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<" + type + ">)"); |
| |
| // Null column |
| execute("INSERT INTO %s (k) VALUES (0) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_1, TTL_1); |
| assertWritetimeAndTTL("t", NO_TIMESTAMP, NO_TTL); |
| |
| // Both fields are empty |
| execute("INSERT INTO %s (k, t) VALUES (0, {f1:null, f2:null}) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_1, TTL_1); |
| assertWritetimeAndTTL("t", "k=0", TIMESTAMP_1, TTL_1); |
| |
| // Only the first field is set |
| execute("INSERT INTO %s (k, t) VALUES (1, {f1:1, f2:null}) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_1, TTL_1); |
| assertWritetimeAndTTL("t", "k=1", TIMESTAMP_1, TTL_1); |
| |
| // Only the second field is set |
| execute("INSERT INTO %s (k, t) VALUES (2, {f1:null, f2:2}) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_1, TTL_1); |
| assertWritetimeAndTTL("t", "k=2", TIMESTAMP_1, TTL_1); |
| |
| // Both fields are set |
| execute("INSERT INTO %s (k, t) VALUES (3, {f1:1, f2:2}) USING TIMESTAMP ? AND TTL ?", TIMESTAMP_1, TTL_1); |
| assertWritetimeAndTTL("t", "k=3", TIMESTAMP_1, TTL_1); |
| } |
| |
| private void assertRows(String query, Object[]... rows) throws Throwable |
| { |
| assertRows(execute(query), rows); |
| } |
| |
| private void assertWritetimeAndTTL(String column, Long timestamp, Integer ttl) throws Throwable |
| { |
| assertWritetimeAndTTL(column, null, timestamp, ttl); |
| } |
| |
| private void assertWritetimeAndTTL(String column, String where, Long timestamp, Integer ttl) |
| throws Throwable |
| { |
| where = where == null ? "" : " WHERE " + where; |
| |
| // Verify write time |
| String writetimeQuery = String.format("SELECT WRITETIME(%s) FROM %%s %s", column, where); |
| assertRows(writetimeQuery, row(timestamp)); |
| |
| // Verify ttl |
| UntypedResultSet rs = execute(String.format("SELECT TTL(%s) FROM %%s %s", column, where)); |
| assertRowCount(rs, 1); |
| UntypedResultSet.Row row = rs.one(); |
| String ttlColumn = String.format("ttl(%s)", column); |
| if (ttl == null) |
| { |
| assertTTL(ttl, null); |
| } |
| else |
| { |
| assertTTL(ttl, row.getInt(ttlColumn)); |
| } |
| } |
| |
| /** |
| * Since the returned TTL is the remaining seconds since last update, it could be lower than the |
| * specified TTL depending on the test execution time, se we allow up to one-minute difference |
| */ |
| private void assertTTL(Integer expected, Integer actual) |
| { |
| if (expected == null) |
| { |
| assertNull(actual); |
| } |
| else |
| { |
| assertNotNull(actual); |
| assertTrue(actual > expected - 60); |
| assertTrue(actual <= expected); |
| } |
| } |
| |
| private void assertInvalidPrimaryKeySelection(String column) throws Throwable |
| { |
| assertInvalidThrowMessage("Cannot use selection function writeTime on PRIMARY KEY part " + column, |
| InvalidRequestException.class, |
| String.format("SELECT WRITETIME(%s) FROM %%s", column)); |
| assertInvalidThrowMessage("Cannot use selection function ttl on PRIMARY KEY part " + column, |
| InvalidRequestException.class, |
| String.format("SELECT TTL(%s) FROM %%s", column)); |
| } |
| |
| private void assertInvalidMultiCellSelection(String column) throws Throwable |
| { |
| String message = "Cannot use selection function %s on non-frozen collection " + column; |
| assertInvalidThrowMessage(format(message, "writeTime"), |
| InvalidRequestException.class, |
| String.format("SELECT WRITETIME(%s) FROM %%s", column)); |
| assertInvalidThrowMessage(format(message, "ttl"), |
| InvalidRequestException.class, |
| String.format("SELECT TTL(%s) FROM %%s", column)); |
| } |
| } |