mirror of https://github.com/apache/cassandra
344 lines
13 KiB
Java
344 lines
13 KiB
Java
/*
|
|
* 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;
|
|
|
|
import java.util.List;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import com.datastax.driver.core.ResultSet;
|
|
import com.datastax.driver.core.Row;
|
|
|
|
import org.assertj.core.api.Assertions;
|
|
import org.junit.Assert;
|
|
import org.junit.Test;
|
|
|
|
import org.apache.cassandra.Util;
|
|
import org.apache.cassandra.db.Keyspace;
|
|
import org.apache.cassandra.exceptions.InvalidRequestException;
|
|
|
|
import static org.junit.Assert.assertEquals;
|
|
import static org.junit.Assert.fail;
|
|
|
|
/*
|
|
* This test class was too large and used to timeout CASSANDRA-16777. We're splitting it into:
|
|
* - ViewTest
|
|
* - ViewPKTest
|
|
* - ViewRangesTest
|
|
* - ViewTimesTest
|
|
*/
|
|
public class ViewTimesTest extends ViewAbstractTest
|
|
{
|
|
@Test
|
|
public void testRegularColumnTimestampUpdates() throws Throwable
|
|
{
|
|
// Regression test for CASSANDRA-10910
|
|
|
|
createTable("CREATE TABLE %s (" +
|
|
"k int PRIMARY KEY, " +
|
|
"c int, " +
|
|
"val int)");
|
|
|
|
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s " +
|
|
"WHERE k IS NOT NULL AND c IS NOT NULL " +
|
|
"PRIMARY KEY (k,c)");
|
|
|
|
updateView("UPDATE %s SET c = ?, val = ? WHERE k = ?", 0, 0, 0);
|
|
updateView("UPDATE %s SET val = ? WHERE k = ?", 1, 0);
|
|
updateView("UPDATE %s SET c = ? WHERE k = ?", 1, 0);
|
|
assertRows(executeView("SELECT c, k, val FROM %s"), row(1, 0, 1));
|
|
|
|
updateView("TRUNCATE %s");
|
|
|
|
updateView("UPDATE %s USING TIMESTAMP 1 SET c = ?, val = ? WHERE k = ?", 0, 0, 0);
|
|
updateView("UPDATE %s USING TIMESTAMP 3 SET c = ? WHERE k = ?", 1, 0);
|
|
updateView("UPDATE %s USING TIMESTAMP 2 SET val = ? WHERE k = ?", 1, 0);
|
|
updateView("UPDATE %s USING TIMESTAMP 4 SET c = ? WHERE k = ?", 2, 0);
|
|
updateView("UPDATE %s USING TIMESTAMP 3 SET val = ? WHERE k = ?", 2, 0);
|
|
|
|
assertRows(executeView("SELECT c, k, val FROM %s"), row(2, 0, 2));
|
|
assertRows(executeView("SELECT c, k, val FROM %s limit 1"), row(2, 0, 2));
|
|
}
|
|
|
|
@Test
|
|
public void complexTimestampUpdateTestWithFlush() throws Throwable
|
|
{
|
|
complexTimestampUpdateTest(true);
|
|
}
|
|
|
|
@Test
|
|
public void complexTimestampUpdateTestWithoutFlush() throws Throwable
|
|
{
|
|
complexTimestampUpdateTest(false);
|
|
}
|
|
|
|
public void complexTimestampUpdateTest(boolean flush) throws Throwable
|
|
{
|
|
createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, PRIMARY KEY (a, b))");
|
|
|
|
Keyspace ks = Keyspace.open(keyspace());
|
|
|
|
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s " +
|
|
"WHERE a IS NOT NULL AND b IS NOT NULL AND c IS NOT NULL " +
|
|
"PRIMARY KEY (c, a, b)");
|
|
ks.getColumnFamilyStore(currentView()).disableAutoCompaction();
|
|
|
|
//Set initial values TS=0, leaving e null and verify view
|
|
executeNet("INSERT INTO %s (a, b, c, d) VALUES (0, 0, 1, 0) USING TIMESTAMP 0");
|
|
assertRows(executeView("SELECT d from %s WHERE c = ? and a = ? and b = ?", 1, 0, 0), row(0));
|
|
|
|
//update c's timestamp TS=2
|
|
executeNet("UPDATE %s USING TIMESTAMP 2 SET c = ? WHERE a = ? and b = ? ", 1, 0, 0);
|
|
assertRows(executeView("SELECT d from %s WHERE c = ? and a = ? and b = ?", 1, 0, 0), row(0));
|
|
|
|
if (flush)
|
|
Util.flush(ks);
|
|
|
|
// change c's value and TS=3, tombstones c=1 and adds c=0 record
|
|
executeNet("UPDATE %s USING TIMESTAMP 3 SET c = ? WHERE a = ? and b = ? ", 0, 0, 0);
|
|
if (flush)
|
|
Util.flush(ks);
|
|
assertRows(executeView("SELECT d from %s WHERE c = ? and a = ? and b = ?", 1, 0, 0));
|
|
|
|
if(flush)
|
|
{
|
|
ks.getColumnFamilyStore(currentView()).forceMajorCompaction();
|
|
Util.flush(ks);
|
|
}
|
|
|
|
|
|
//change c's value back to 1 with TS=4, check we can see d
|
|
executeNet("UPDATE %s USING TIMESTAMP 4 SET c = ? WHERE a = ? and b = ? ", 1, 0, 0);
|
|
if (flush)
|
|
{
|
|
ks.getColumnFamilyStore(currentView()).forceMajorCompaction();
|
|
Util.flush(ks);
|
|
}
|
|
|
|
assertRows(executeView("SELECT d,e from %s WHERE c = ? and a = ? and b = ?", 1, 0, 0), row(0, null));
|
|
|
|
//Add e value @ TS=1
|
|
executeNet("UPDATE %s USING TIMESTAMP 1 SET e = ? WHERE a = ? and b = ? ", 1, 0, 0);
|
|
assertRows(executeView("SELECT d,e from %s WHERE c = ? and a = ? and b = ?", 1, 0, 0), row(0, 1));
|
|
|
|
if (flush)
|
|
Util.flush(ks);
|
|
|
|
|
|
//Change d value @ TS=2
|
|
executeNet("UPDATE %s USING TIMESTAMP 2 SET d = ? WHERE a = ? and b = ? ", 2, 0, 0);
|
|
assertRows(executeView("SELECT d from %s WHERE c = ? and a = ? and b = ?", 1, 0, 0), row(2));
|
|
|
|
if (flush)
|
|
Util.flush(ks);
|
|
|
|
//Change d value @ TS=3
|
|
executeNet("UPDATE %s USING TIMESTAMP 3 SET d = ? WHERE a = ? and b = ? ", 1, 0, 0);
|
|
assertRows(executeView("SELECT d from %s WHERE c = ? and a = ? and b = ?", 1, 0, 0), row(1));
|
|
|
|
|
|
//Tombstone c
|
|
executeNet("DELETE FROM %s WHERE a = ? and b = ?", 0, 0);
|
|
assertRows(executeView("SELECT d from %s"));
|
|
|
|
//Add back without D
|
|
executeNet("INSERT INTO %s (a, b, c) VALUES (0, 0, 1)");
|
|
|
|
//Make sure D doesn't pop back in.
|
|
assertRows(executeView("SELECT d from %s WHERE c = ? and a = ? and b = ?", 1, 0, 0), row((Object) null));
|
|
|
|
|
|
//New partition
|
|
// insert a row with timestamp 0
|
|
executeNet("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?) USING TIMESTAMP 0", 1, 0, 0, 0, 0);
|
|
|
|
// overwrite pk and e with timestamp 1, but don't overwrite d
|
|
executeNet("INSERT INTO %s (a, b, c, e) VALUES (?, ?, ?, ?) USING TIMESTAMP 1", 1, 0, 0, 0);
|
|
|
|
// delete with timestamp 0 (which should only delete d)
|
|
executeNet("DELETE FROM %s USING TIMESTAMP 0 WHERE a = ? AND b = ?", 1, 0);
|
|
assertRows(executeView("SELECT a, b, c, d, e from %s WHERE c = ? and a = ? and b = ?", 0, 1, 0),
|
|
row(1, 0, 0, null, 0));
|
|
|
|
executeNet("UPDATE %s USING TIMESTAMP 2 SET c = ? WHERE a = ? AND b = ?", 1, 1, 0);
|
|
executeNet("UPDATE %s USING TIMESTAMP 3 SET c = ? WHERE a = ? AND b = ?", 0, 1, 0);
|
|
assertRows(executeView("SELECT a, b, c, d, e from %s WHERE c = ? and a = ? and b = ?", 0, 1, 0),
|
|
row(1, 0, 0, null, 0));
|
|
|
|
executeNet("UPDATE %s USING TIMESTAMP 3 SET d = ? WHERE a = ? AND b = ?", 0, 1, 0);
|
|
assertRows(executeView("SELECT a, b, c, d, e from %s WHERE c = ? and a = ? and b = ?", 0, 1, 0),
|
|
row(1, 0, 0, 0, 0));
|
|
}
|
|
|
|
@Test
|
|
public void ttlTest() throws Throwable
|
|
{
|
|
createTable("CREATE TABLE %s (" +
|
|
"a int," +
|
|
"b int," +
|
|
"c int," +
|
|
"d int," +
|
|
"PRIMARY KEY (a, b))");
|
|
|
|
executeNet("USE " + keyspace());
|
|
|
|
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE c IS NOT NULL AND a IS NOT NULL AND b IS NOT NULL PRIMARY KEY (c, a, b)");
|
|
|
|
updateView("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?) USING TTL 3", 1, 1, 1, 1);
|
|
|
|
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
|
|
updateView("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 1, 1, 2);
|
|
|
|
Thread.sleep(TimeUnit.SECONDS.toMillis(5));
|
|
List<Row> results = executeViewNet("SELECT d FROM %s WHERE c = 2 AND a = 1 AND b = 1").all();
|
|
Assert.assertEquals(1, results.size());
|
|
Assert.assertTrue("There should be a null result given back due to ttl expiry", results.get(0).isNull(0));
|
|
}
|
|
|
|
@Test
|
|
public void ttlExpirationTest() throws Throwable
|
|
{
|
|
createTable("CREATE TABLE %s (" +
|
|
"a int," +
|
|
"b int," +
|
|
"c int," +
|
|
"d int," +
|
|
"PRIMARY KEY (a, b))");
|
|
|
|
executeNet("USE " + keyspace());
|
|
|
|
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE c IS NOT NULL AND a IS NOT NULL AND b IS NOT NULL PRIMARY KEY (c, a, b)");
|
|
|
|
updateView("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?) USING TTL 3", 1, 1, 1, 1);
|
|
|
|
Thread.sleep(TimeUnit.SECONDS.toMillis(4));
|
|
Assert.assertEquals(0, executeViewNet("SELECT * FROM %s WHERE c = 1 AND a = 1 AND b = 1").all().size());
|
|
}
|
|
|
|
@Test
|
|
public void conflictingTimestampTest() throws Throwable
|
|
{
|
|
createTable("CREATE TABLE %s (" +
|
|
"a int," +
|
|
"b int," +
|
|
"c int," +
|
|
"PRIMARY KEY (a, b))");
|
|
|
|
executeNet("USE " + keyspace());
|
|
|
|
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE c IS NOT NULL AND a IS NOT NULL AND b IS NOT NULL PRIMARY KEY (c, a, b)");
|
|
|
|
for (int i = 0; i < 50; i++)
|
|
{
|
|
updateView("INSERT INTO %s (a, b, c) VALUES (?, ?, ?) USING TIMESTAMP 1", 1, 1, i);
|
|
}
|
|
|
|
ResultSet mvRows = executeViewNet("SELECT c FROM %s");
|
|
List<Row> rows = executeNet("SELECT c FROM %s").all();
|
|
assertEquals("There should be exactly one row in base", 1, rows.size());
|
|
int expected = rows.get(0).getInt("c");
|
|
assertRowsNet(mvRows, row(expected));
|
|
}
|
|
|
|
@Test
|
|
public void testCreateMvWithTTL()
|
|
{
|
|
createTable("CREATE TABLE %s (" +
|
|
"k int PRIMARY KEY, " +
|
|
"c int, " +
|
|
"val int) WITH default_time_to_live = 60");
|
|
|
|
// Must NOT include "default_time_to_live" for Materialized View creation
|
|
try
|
|
{
|
|
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s " +
|
|
"WHERE k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (k,c) WITH default_time_to_live = 30");
|
|
fail("Should fail if TTL is provided for materialized view");
|
|
}
|
|
catch (RuntimeException e)
|
|
{
|
|
Throwable cause = e.getCause();
|
|
Assertions.assertThat(cause).isInstanceOf(InvalidRequestException.class);
|
|
Assertions.assertThat(cause.getMessage()).contains("Cannot set default_time_to_live for a materialized view");
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testAlterMvWithNoZeroTTL() throws Throwable
|
|
{
|
|
createTable("CREATE TABLE %s (" +
|
|
"k int PRIMARY KEY, " +
|
|
"c int, " +
|
|
"val int) WITH default_time_to_live = 60");
|
|
|
|
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (k,c)");
|
|
|
|
// Must NOT include "default_time_to_live" on alter Materialized View
|
|
try
|
|
{
|
|
executeNet("ALTER MATERIALIZED VIEW " + currentView() + " WITH default_time_to_live = 30");
|
|
fail("Should fail if TTL is provided while altering materialized view");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// Make sure the message is clear. See CASSANDRA-16960
|
|
assertEquals("Forbidden default_time_to_live detected for a materialized view. Data in a materialized view always expire at the same time than the corresponding "
|
|
+ "data in the parent table. default_time_to_live must be set to zero, see CASSANDRA-12868 for more information",
|
|
e.getMessage());
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testMvWithZeroTTL()
|
|
{
|
|
createTable("CREATE TABLE %s (" +
|
|
"k int PRIMARY KEY, " +
|
|
"c int, " +
|
|
"val int) WITH default_time_to_live = 60");
|
|
|
|
try
|
|
{
|
|
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (k,c) WITH default_time_to_live = 0");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
fail("Should not fail if TTL equal to 0 is provided while altering materialized view");
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testAlterMvWithZeroTTL() throws Throwable
|
|
{
|
|
createTable("CREATE TABLE %s (" +
|
|
"k int PRIMARY KEY, " +
|
|
"c int, " +
|
|
"val int) WITH default_time_to_live = 60");
|
|
|
|
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (k,c)");
|
|
|
|
try
|
|
{
|
|
executeNet("ALTER MATERIALIZED VIEW " + currentView() + " WITH default_time_to_live = 0");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
fail("Should not fail if TTL equal to 0 is provided while altering materialized view");
|
|
}
|
|
}
|
|
}
|