mirror of https://github.com/apache/cassandra
Merge branch 'cassandra-4.1' into trunk
This commit is contained in:
commit
f8207fe287
|
|
@ -25,6 +25,7 @@ Merged from 4.0:
|
||||||
* silence benign SslClosedEngineException (CASSANDRA-17565)
|
* silence benign SslClosedEngineException (CASSANDRA-17565)
|
||||||
Merged from 3.11:
|
Merged from 3.11:
|
||||||
Merged from 3.0:
|
Merged from 3.0:
|
||||||
|
* Fix writetime and ttl functions forbidden for collections instead of multicell columns (CASSANDRA-17628)
|
||||||
|
|
||||||
|
|
||||||
4.1-alpha1
|
4.1-alpha1
|
||||||
|
|
|
||||||
|
|
@ -1711,7 +1711,9 @@ to these generic functions, the `WRITETIME` and `MAXWRITETIME` (resp. `TTL`)
|
||||||
function allows to select the timestamp of when the column was inserted (resp.
|
function allows to select the timestamp of when the column was inserted (resp.
|
||||||
the time to live (in seconds) for the column (or null if the column has
|
the time to live (in seconds) for the column (or null if the column has
|
||||||
no expiration set)) and the link:#castFun[`CAST`] function can be used
|
no expiration set)) and the link:#castFun[`CAST`] function can be used
|
||||||
to convert one data type to another.
|
to convert one data type to another. The `WRITETIME` and `TTL` functions
|
||||||
|
can't be used on multi-cell columns such as non-frozen collections or
|
||||||
|
non-frozen user-defined types.
|
||||||
|
|
||||||
Additionally, individual values of maps and sets can be selected using
|
Additionally, individual values of maps and sets can be selected using
|
||||||
`[ <term> ]`. For maps, this will return the value corresponding to the
|
`[ <term> ]`. For maps, this will return the value corresponding to the
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,9 @@ and UDT.
|
||||||
is equivalent to `WRITETIME`. In the other cases, it returns the largest timestamp of the values in the column.
|
is equivalent to `WRITETIME`. In the other cases, it returns the largest timestamp of the values in the column.
|
||||||
* `TTL` stores the remaining time to live (in seconds) for the value of the column if it is set to expire; otherwise the value is `null`.
|
* `TTL` stores the remaining time to live (in seconds) for the value of the column if it is set to expire; otherwise the value is `null`.
|
||||||
|
|
||||||
|
The `WRITETIME` and `TTL` functions can't be used on multi-cell columns such as non-frozen
|
||||||
|
collections or non-frozen user-defined types.
|
||||||
|
|
||||||
[[where-clause]]
|
[[where-clause]]
|
||||||
=== The `WHERE` clause
|
=== The `WHERE` clause
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -275,10 +275,12 @@ public interface Selectable extends AssignmentTestable
|
||||||
kind.name,
|
kind.name,
|
||||||
column.name));
|
column.name));
|
||||||
|
|
||||||
// only maxwritetime is allowed for collection
|
// only maxwritetime is allowed for multicell types
|
||||||
if (column.type.isCollection() && !kind.allowedForMultiCell())
|
if (column.type.isMultiCell() && !kind.allowedForMultiCell())
|
||||||
throw new InvalidRequestException(String.format("Cannot use selection function %s on collections",
|
throw new InvalidRequestException(String.format("Cannot use selection function %s on non-frozen %s %s",
|
||||||
kind.name));
|
kind.name,
|
||||||
|
column.type.isCollection() ? "collection" : "UDT",
|
||||||
|
column.name));
|
||||||
|
|
||||||
return WritetimeOrTTLSelector.newFactory(column, addAndGetIndex(column, defs), kind);
|
return WritetimeOrTTLSelector.newFactory(column, addAndGetIndex(column, defs), kind);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,272 @@
|
||||||
|
/*
|
||||||
|
* 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", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 testUDT() throws Throwable
|
||||||
|
{
|
||||||
|
String type = createType("CREATE TYPE %s (f1 int, f2 int)");
|
||||||
|
createTable("CREATE TABLE %s (k int PRIMARY KEY, t " + type + ')');
|
||||||
|
assertInvalidMultiCellSelection("t", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 max write time
|
||||||
|
String maxwritetimeQuery = String.format("SELECT MAXWRITETIME(%s) FROM %%s %s", column, where);
|
||||||
|
assertRows(maxwritetimeQuery, 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 maxwritetime on PRIMARY KEY part " + column,
|
||||||
|
InvalidRequestException.class,
|
||||||
|
String.format("SELECT MAXWRITETIME(%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, boolean isCollection) throws Throwable
|
||||||
|
{
|
||||||
|
String message = format("Cannot use selection function %%s on non-frozen %s %s",
|
||||||
|
isCollection ? "collection" : "UDT", column);
|
||||||
|
assertInvalidThrowMessage(format(message, "writetime"),
|
||||||
|
InvalidRequestException.class,
|
||||||
|
String.format("SELECT WRITETIME(%s) FROM %%s", column));
|
||||||
|
execute(format("SELECT MAXWRITETIME(%s) FROM %%s", column));
|
||||||
|
assertInvalidThrowMessage(format(message, "ttl"),
|
||||||
|
InvalidRequestException.class,
|
||||||
|
String.format("SELECT TTL(%s) FROM %%s", column));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue