blob: d82fd322e1fc3b108b40247096fe665defb31aea [file] [log] [blame]
/*
* 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.CountDownLatch;
import java.util.concurrent.Future;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.datastax.driver.core.ResultSet;
import org.apache.cassandra.Util;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.SystemKeyspace;
import org.apache.cassandra.db.compaction.CompactionManager;
import org.apache.cassandra.db.view.View;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.SchemaKeyspaceTables;
import org.apache.cassandra.service.ClientWarn;
import org.apache.cassandra.utils.FBUtilities;
import org.assertj.core.api.Assertions;
import org.awaitility.Awaitility;
import org.jboss.byteman.contrib.bmunit.BMRule;
import org.jboss.byteman.contrib.bmunit.BMRules;
import org.jboss.byteman.contrib.bmunit.BMUnitRunner;
import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/*
* This test class was too large and used to timeout CASSANDRA-16777. We're splitting it into:
* - ViewTest
* - ViewPKTest
* - ViewRangesTest
* - ViewTimesTest
*/
@RunWith(BMUnitRunner.class)
public class ViewTest extends ViewAbstractTest
{
/** Latch used by {@link #testTruncateWhileBuilding()} Byteman injections. */
@SuppressWarnings("unused")
private static final CountDownLatch blockViewBuild = new CountDownLatch(1);
@Test
public void testNonExistingOnes() throws Throwable
{
assertInvalidMessage(String.format("Materialized view '%s.view_does_not_exist' doesn't exist", KEYSPACE), "DROP MATERIALIZED VIEW " + KEYSPACE + ".view_does_not_exist");
assertInvalidMessage("Materialized view 'keyspace_does_not_exist.view_does_not_exist' doesn't exist", "DROP MATERIALIZED VIEW keyspace_does_not_exist.view_does_not_exist");
execute("DROP MATERIALIZED VIEW IF EXISTS " + KEYSPACE + ".view_does_not_exist");
execute("DROP MATERIALIZED VIEW IF EXISTS keyspace_does_not_exist.view_does_not_exist");
}
@Test
public void testStaticTable() throws Throwable
{
createTable("CREATE TABLE %s (" +
"k int, " +
"c int, " +
"sval text static, " +
"val text, " +
"PRIMARY KEY(k,c))");
try
{
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE sval IS NOT NULL AND k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (sval,k,c)");
Assert.fail("Use of static column in a MV primary key should fail");
}
catch (Exception e)
{
Assert.assertTrue(e.getCause() instanceof InvalidRequestException);
}
try
{
createView("CREATE MATERIALIZED VIEW %s AS SELECT val, sval FROM %s WHERE val IS NOT NULL AND k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (val, k, c)");
Assert.fail("Explicit select of static column in MV should fail");
}
catch (Exception e)
{
Assert.assertTrue(e.getCause() instanceof InvalidRequestException);
}
try
{
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE val IS NOT NULL AND k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (val,k,c)");
Assert.fail("Implicit select of static column in MV should fail");
}
catch (Exception e)
{
Assert.assertTrue(e.getCause() instanceof InvalidRequestException);
}
createView("CREATE MATERIALIZED VIEW %s AS SELECT val,k,c FROM %s WHERE val IS NOT NULL AND k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (val,k,c)");
for (int i = 0; i < 100; i++)
updateView("INSERT into %s (k,c,sval,val)VALUES(?,?,?,?)", 0, i % 2, "bar" + i, "baz");
Assert.assertEquals(2, execute("select * from %s").size());
assertRows(execute("SELECT sval from %s"), row("bar99"), row("bar99"));
Assert.assertEquals(2, executeView("select * from %s").size());
assertInvalid("SELECT sval from " + currentView());
}
@Test
public void testOldTimestamps() throws Throwable
{
createTable("CREATE TABLE %s (" +
"k int, " +
"c int, " +
"val text, " +
"PRIMARY KEY(k,c))");
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE val IS NOT NULL AND k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (val,k,c)");
for (int i = 0; i < 100; i++)
updateView("INSERT into %s (k,c,val)VALUES(?,?,?)", 0, i % 2, "baz");
Keyspace.open(keyspace()).getColumnFamilyStore(currentTable()).forceBlockingFlush(ColumnFamilyStore.FlushReason.UNIT_TESTS);
Assert.assertEquals(2, execute("select * from %s").size());
Assert.assertEquals(2, executeView("select * from %s").size());
assertRows(execute("SELECT val from %s where k = 0 and c = 0"), row("baz"));
assertRows(executeView("SELECT c from %s where k = 0 and val = ?", "baz"), row(0), row(1));
//Make sure an old TS does nothing
updateView("UPDATE %s USING TIMESTAMP 100 SET val = ? where k = ? AND c = ?", "bar", 0, 0);
assertRows(execute("SELECT val from %s where k = 0 and c = 0"), row("baz"));
assertRows(executeView("SELECT c from %s where k = 0 and val = ?", "baz"), row(0), row(1));
assertRows(executeView("SELECT c from %s where k = 0 and val = ?", "bar"));
//Latest TS
updateView("UPDATE %s SET val = ? where k = ? AND c = ?", "bar", 0, 0);
assertRows(execute("SELECT val from %s where k = 0 and c = 0"), row("bar"));
assertRows(executeView("SELECT c from %s where k = 0 and val = ?", "bar"), row(0));
assertRows(executeView("SELECT c from %s where k = 0 and val = ?", "baz"), row(1));
}
@Test
public void testCountersTable()
{
createTable("CREATE TABLE %s (" +
"k int PRIMARY KEY, " +
"count counter)");
try
{
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE count IS NOT NULL AND k IS NOT NULL PRIMARY KEY (count,k)");
Assert.fail("MV on counter should fail");
}
catch (Exception e)
{
Assert.assertTrue(e.getCause() instanceof InvalidRequestException);
}
}
@Test
public void testDurationsTable()
{
createTable("CREATE TABLE %s (" +
"k int PRIMARY KEY, " +
"result duration)");
try
{
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE result IS NOT NULL AND k IS NOT NULL PRIMARY KEY (result,k)");
Assert.fail("MV on duration should fail");
}
catch (Exception e)
{
Throwable cause = e.getCause();
Assert.assertEquals("duration type is not supported for PRIMARY KEY column 'result'", cause.getMessage());
}
}
@Test
public void testBuilderWidePartition() throws Throwable
{
createTable("CREATE TABLE %s (" +
"k int, " +
"c int, " +
"intval int, " +
"PRIMARY KEY (k, c))");
for (int i = 0; i < 1024; i++)
execute("INSERT INTO %s (k, c, intval) VALUES (?, ?, ?)", 0, i, 0);
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE k IS NOT NULL AND c IS NOT NULL AND intval IS NOT NULL PRIMARY KEY (intval, c, k)");
assertRows(execute("SELECT count(*) from %s WHERE k = ?", 0), row(1024L));
assertRows(executeView("SELECT count(*) from %s WHERE intval = ?", 0), row(1024L));
}
@Test
public void testCollections() throws Throwable
{
createTable("CREATE TABLE %s (" +
"k int, " +
"intval int, " +
"listval list<int>, " +
"PRIMARY KEY (k))");
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE k IS NOT NULL AND intval IS NOT NULL PRIMARY KEY (intval, k)");
updateView("INSERT INTO %s (k, intval, listval) VALUES (?, ?, fromJson(?))", 0, 0, "[1, 2, 3]");
assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0), row(0, list(1, 2, 3)));
assertRows(executeView("SELECT k, listval from %s WHERE intval = ?", 0), row(0, list(1, 2, 3)));
updateView("INSERT INTO %s (k, intval) VALUES (?, ?)", 1, 1);
updateView("INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 1, "[1, 2, 3]");
assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 1), row(1, list(1, 2, 3)));
assertRows(executeView("SELECT k, listval from %s WHERE intval = ?", 1), row(1, list(1, 2, 3)));
}
@Test
public void testFrozenCollectionsWithComplicatedInnerType() throws Throwable
{
createTable("CREATE TABLE %s (k int, intval int, listval frozen<list<tuple<text,text>>>, PRIMARY KEY (k))");
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE k IS NOT NULL AND listval IS NOT NULL PRIMARY KEY (k, listval)");
updateView("INSERT INTO %s (k, intval, listval) VALUES (?, ?, fromJson(?))",
0,
0,
"[[\"a\",\"1\"], [\"b\",\"2\"], [\"c\",\"3\"]]");
// verify input
assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0),
row(0, list(tuple("a", "1"), tuple("b", "2"), tuple("c", "3"))));
assertRows(executeView("SELECT k, listval from %s"),
row(0, list(tuple("a", "1"), tuple("b", "2"), tuple("c", "3"))));
// update listval with the same value and it will be compared in view generator
updateView("INSERT INTO %s (k, listval) VALUES (?, fromJson(?))",
0,
"[[\"a\",\"1\"], [\"b\",\"2\"], [\"c\",\"3\"]]");
// verify result
assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0),
row(0, list(tuple("a", "1"), tuple("b", "2"), tuple("c", "3"))));
assertRows(executeView("SELECT k, listval from %s"),
row(0, list(tuple("a", "1"), tuple("b", "2"), tuple("c", "3"))));
}
@Test
public void testUpdate() throws Throwable
{
createTable("CREATE TABLE %s (" +
"k int, " +
"intval int, " +
"PRIMARY KEY (k))");
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE k IS NOT NULL AND intval IS NOT NULL PRIMARY KEY (intval, k)");
updateView("INSERT INTO %s (k, intval) VALUES (?, ?)", 0, 0);
assertRows(execute("SELECT k, intval FROM %s WHERE k = ?", 0), row(0, 0));
assertRows(executeView("SELECT k, intval from %s WHERE intval = ?", 0), row(0, 0));
updateView("INSERT INTO %s (k, intval) VALUES (?, ?)", 0, 1);
assertRows(execute("SELECT k, intval FROM %s WHERE k = ?", 0), row(0, 1));
assertRows(executeView("SELECT k, intval from %s WHERE intval = ?", 1), row(0, 1));
}
@Test
public void testIgnoreUpdate() throws Throwable
{
// regression test for CASSANDRA-10614
createTable("CREATE TABLE %s (" +
"a int, " +
"b int, " +
"c int, " +
"d int, " +
"PRIMARY KEY (a, b))");
createView("CREATE MATERIALIZED VIEW %s AS SELECT a, b, c FROM %s WHERE a IS NOT NULL AND b IS NOT NULL PRIMARY KEY (b, a)");
updateView("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 0, 0);
assertRows(executeView("SELECT a, b, c from %s WHERE b = ?", 0), row(0, 0, 0));
updateView("UPDATE %s SET d = ? WHERE a = ? AND b = ?", 0, 0, 0);
assertRows(executeView("SELECT a, b, c from %s WHERE b = ?", 0), row(0, 0, 0));
// Note: errors here may result in the test hanging when the memtables are flushed as part of the table drop,
// because empty rows in the memtable will cause the flush to fail. This will result in a test timeout that
// should not be ignored.
String table = KEYSPACE + "." + currentTable();
updateView("BEGIN BATCH " +
"INSERT INTO " + table + " (a, b, c, d) VALUES (?, ?, ?, ?); " + // should be accepted
"UPDATE " + table + " SET d = ? WHERE a = ? AND b = ?; " + // should be accepted
"APPLY BATCH",
0, 0, 0, 0,
1, 0, 1);
assertRows(executeView("SELECT a, b, c from %s WHERE b = ?", 0), row(0, 0, 0));
assertRows(executeView("SELECT a, b, c from %s WHERE b = ?", 1), row(0, 1, null));
ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(currentView());
Util.flush(cfs);
Assert.assertEquals(1, cfs.getLiveSSTables().size());
}
@Test
public void rowDeletionTest() 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)");
String table = keyspace() + "." + currentTable();
updateView("DELETE FROM " + table + " USING TIMESTAMP 6 WHERE a = 1 AND b = 1;");
updateView("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?) USING TIMESTAMP 3", 1, 1, 1, 1);
Assert.assertEquals(0, executeViewNet("SELECT * FROM %s WHERE c = 1 AND a = 1 AND b = 1").all().size());
}
@Test
public void testMultipleDeletes() throws Throwable
{
createTable("CREATE TABLE %s (" +
"a int," +
"b int," +
"PRIMARY KEY (a, b))");
executeNet("USE " + keyspace());
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE a IS NOT NULL AND b IS NOT NULL PRIMARY KEY (b, a)");
updateView("INSERT INTO %s (a, b) VALUES (?, ?)", 1, 1);
updateView("INSERT INTO %s (a, b) VALUES (?, ?)", 1, 2);
updateView("INSERT INTO %s (a, b) VALUES (?, ?)", 1, 3);
ResultSet mvRows = executeViewNet("SELECT a, b FROM %s");
assertRowsNet(mvRows, row(1, 1), row(1, 2), row(1, 3));
updateView(String.format("BEGIN UNLOGGED BATCH " +
"DELETE FROM %s WHERE a = 1 AND b > 1 AND b < 3;" +
"DELETE FROM %s WHERE a = 1;" +
"APPLY BATCH", currentTable(), currentTable()));
mvRows = executeViewNet("SELECT a, b FROM %s");
assertRowsNet(mvRows);
}
@Test
public void testCollectionInView() throws Throwable
{
createTable("CREATE TABLE %s (" +
"a int," +
"b int," +
"c map<int, text>," +
"PRIMARY KEY (a))");
executeNet("USE " + keyspace());
createView("CREATE MATERIALIZED VIEW %s AS SELECT a, b FROM %s WHERE a IS NOT NULL AND b IS NOT NULL PRIMARY KEY (b, a)");
updateView("INSERT INTO %s (a, b) VALUES (?, ?)", 0, 0);
ResultSet mvRows = executeViewNet("SELECT a, b FROM %s WHERE b = ?", 0);
assertRowsNet(mvRows, row(0, 0));
updateView("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 1, 1, map(1, "1"));
mvRows = executeViewNet("SELECT a, b FROM %s WHERE b = ?", 1);
assertRowsNet(mvRows, row(1, 1));
updateView("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 0, map(0, "0"));
mvRows = executeViewNet("SELECT a, b FROM %s WHERE b = ?", 0);
assertRowsNet(mvRows, row(0, 0));
}
@Test
public void testReservedKeywordsInMV() throws Throwable
{
createTable("CREATE TABLE %s (\"token\" int PRIMARY KEY, \"keyspace\" int)");
executeNet("USE " + keyspace());
createView("CREATE MATERIALIZED VIEW %s AS" +
" SELECT \"keyspace\", \"token\"" +
" FROM %s" +
" WHERE \"keyspace\" IS NOT NULL AND \"token\" IS NOT NULL" +
" PRIMARY KEY (\"keyspace\", \"token\")");
execute("INSERT INTO %s (\"token\", \"keyspace\") VALUES (?, ?)", 0, 1);
assertRowsNet(executeNet("SELECT * FROM %s"), row(0, 1));
assertRowsNet(executeViewNet("SELECT * FROM %s"), row(1, 0));
}
private void testViewBuilderResume(int concurrentViewBuilders) throws Throwable
{
createTable("CREATE TABLE %s (" +
"k int, " +
"c int, " +
"val text, " +
"PRIMARY KEY(k,c))");
CompactionManager.instance.setConcurrentViewBuilders(concurrentViewBuilders);
CompactionManager.instance.setCoreCompactorThreads(1);
CompactionManager.instance.setMaximumCompactorThreads(1);
ColumnFamilyStore cfs = getCurrentColumnFamilyStore();
cfs.disableAutoCompaction();
for (int i = 0; i < 1024; i++)
execute("INSERT into %s (k,c,val)VALUES(?,?,?)", i, i, String.valueOf(i));
Util.flush(cfs);
for (int i = 0; i < 1024; i++)
execute("INSERT into %s (k,c,val)VALUES(?,?,?)", i, i, String.valueOf(i));
Util.flush(cfs);
for (int i = 0; i < 1024; i++)
execute("INSERT into %s (k,c,val)VALUES(?,?,?)", i, i, String.valueOf(i));
Util.flush(cfs);
for (int i = 0; i < 1024; i++)
execute("INSERT into %s (k,c,val)VALUES(?,?,?)", i, i, String.valueOf(i));
Util.flush(cfs);
String mv1 = createViewAsync("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s " +
"WHERE val IS NOT NULL AND k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (val,k,c)");
cfs.enableAutoCompaction();
List<? extends Future<?>> futures = CompactionManager.instance.submitBackground(cfs);
//Force a second MV on the same base table, which will restart the first MV builder...
createView("CREATE MATERIALIZED VIEW %s AS SELECT val, k, c FROM %s " +
"WHERE val IS NOT NULL AND k IS NOT NULL AND c IS NOT NULL PRIMARY KEY (val,k,c)");
//Compact the base table
FBUtilities.waitOnFutures(futures);
waitForViewBuild(mv1);
assertRows(executeView("SELECT count(*) FROM %s"), row(1024L));
}
@Test
public void testViewBuilderResume() throws Throwable
{
for (int i = 1; i <= 8; i *= 2)
{
testViewBuilderResume(i);
}
}
/**
* Tests that a client warning is issued on materialized view creation.
*/
@Test
public void testClientWarningOnCreate()
{
createTable("CREATE TABLE %s (k int PRIMARY KEY, v int)");
ClientWarn.instance.captureWarnings();
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s " +
"WHERE k IS NOT NULL AND v IS NOT NULL PRIMARY KEY (v, k)");
List<String> warnings = ClientWarn.instance.getWarnings();
Assert.assertNotNull(warnings);
Assert.assertEquals(1, warnings.size());
Assert.assertEquals(View.USAGE_WARNING, warnings.get(0));
}
/**
* Tests the configuration flag to disable materialized views.
*/
@Test
public void testDisableMaterializedViews() throws Throwable
{
createTable("CREATE TABLE %s (k int PRIMARY KEY, v int)");
executeNet("USE " + keyspace());
boolean enableMaterializedViews = DatabaseDescriptor.getMaterializedViewsEnabled();
try
{
DatabaseDescriptor.setMaterializedViewsEnabled(false);
createView("CREATE MATERIALIZED VIEW %s AS SELECT v FROM %s WHERE k IS NOT NULL AND v IS NOT NULL PRIMARY KEY (v, k)");
Assert.fail("Should not be able to create a materialized view if they are disabled");
}
catch (RuntimeException e)
{
Throwable cause = e.getCause();
Assertions.assertThat(cause).isInstanceOf(InvalidRequestException.class);
Assertions.assertThat(cause.getMessage()).contains("Materialized views are disabled");
}
finally
{
DatabaseDescriptor.setMaterializedViewsEnabled(enableMaterializedViews);
}
}
@Test
public void testQuotedIdentifiersInWhereClause() throws Throwable
{
createTable("CREATE TABLE %s (\"theKey\" int, \"theClustering_1\" int, \"theClustering_2\" int, \"theValue\" int, PRIMARY KEY (\"theKey\", \"theClustering_1\", \"theClustering_2\"))");
executeNet("USE " + keyspace());
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE \"theKey\" IS NOT NULL AND \"theClustering_1\" IS NOT NULL AND \"theClustering_2\" IS NOT NULL AND \"theValue\" IS NOT NULL PRIMARY KEY (\"theKey\", \"theClustering_1\", \"theClustering_2\");");
createView("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s WHERE \"theKey\" IS NOT NULL AND (\"theClustering_1\", \"theClustering_2\") = (1, 2) AND \"theValue\" IS NOT NULL PRIMARY KEY (\"theKey\", \"theClustering_1\", \"theClustering_2\");");
assertRows(execute("SELECT where_clause FROM system_schema.views"),
row("\"theKey\" IS NOT NULL AND \"theClustering_1\" IS NOT NULL AND \"theClustering_2\" IS NOT NULL AND \"theValue\" IS NOT NULL"),
row("\"theKey\" IS NOT NULL AND (\"theClustering_1\", \"theClustering_2\") = (1, 2) AND \"theValue\" IS NOT NULL"));
}
@Test(expected = SyntaxException.class)
public void emptyViewNameTest() throws Throwable
{
execute("CREATE MATERIALIZED VIEW \"\" AS SELECT a, b FROM tbl WHERE b IS NOT NULL PRIMARY KEY (b, a)");
}
@Test(expected = SyntaxException.class)
public void emptyBaseTableNameTest() throws Throwable
{
execute("CREATE MATERIALIZED VIEW myview AS SELECT a, b FROM \"\" WHERE b IS NOT NULL PRIMARY KEY (b, a)");
}
@Test
public void testFunctionInWhereClause() throws Throwable
{
// Native token function with lowercase, should be unquoted in the schema where clause
assertEmpty(testFunctionInWhereClause("CREATE TABLE %s (k bigint PRIMARY KEY, v int)",
null,
"CREATE MATERIALIZED VIEW %s AS" +
" SELECT * FROM %s WHERE k = token(1) AND v IS NOT NULL " +
" PRIMARY KEY (v, k)",
"k = token(1) AND v IS NOT NULL",
"INSERT INTO %s(k, v) VALUES (0, 1)",
"INSERT INTO %s(k, v) VALUES (2, 3)"));
// Native token function with uppercase, should be unquoted and lowercased in the schema where clause
assertEmpty(testFunctionInWhereClause("CREATE TABLE %s (k bigint PRIMARY KEY, v int)",
null,
"CREATE MATERIALIZED VIEW %s AS" +
" SELECT * FROM %s WHERE k = TOKEN(1) AND v IS NOT NULL" +
" PRIMARY KEY (v, k)",
"k = token(1) AND v IS NOT NULL",
"INSERT INTO %s(k, v) VALUES (0, 1)",
"INSERT INTO %s(k, v) VALUES (2, 3)"));
// UDF with lowercase name, shouldn't be quoted in the schema where clause
assertRows(testFunctionInWhereClause("CREATE TABLE %s (k int PRIMARY KEY, v int)",
"CREATE FUNCTION fun()" +
" CALLED ON NULL INPUT" +
" RETURNS int LANGUAGE java" +
" AS 'return 2;'",
"CREATE MATERIALIZED VIEW %s AS " +
" SELECT * FROM %s WHERE k = fun() AND v IS NOT NULL" +
" PRIMARY KEY (v, k)",
"k = fun() AND v IS NOT NULL",
"INSERT INTO %s(k, v) VALUES (0, 1)",
"INSERT INTO %s(k, v) VALUES (2, 3)"), row(3, 2));
// UDF with uppercase name, should be quoted in the schema where clause
assertRows(testFunctionInWhereClause("CREATE TABLE %s (k int PRIMARY KEY, v int)",
"CREATE FUNCTION \"FUN\"()" +
" CALLED ON NULL INPUT" +
" RETURNS int" +
" LANGUAGE java" +
" AS 'return 2;'",
"CREATE MATERIALIZED VIEW %s AS " +
" SELECT * FROM %s WHERE k = \"FUN\"() AND v IS NOT NULL" +
" PRIMARY KEY (v, k)",
"k = \"FUN\"() AND v IS NOT NULL",
"INSERT INTO %s(k, v) VALUES (0, 1)",
"INSERT INTO %s(k, v) VALUES (2, 3)"), row(3, 2));
// UDF with uppercase name conflicting with TOKEN keyword but not with native token function name,
// should be quoted in the schema where clause
assertRows(testFunctionInWhereClause("CREATE TABLE %s (k int PRIMARY KEY, v int)",
"CREATE FUNCTION \"TOKEN\"(x int)" +
" CALLED ON NULL INPUT" +
" RETURNS int" +
" LANGUAGE java" +
" AS 'return x;'",
"CREATE MATERIALIZED VIEW %s AS" +
" SELECT * FROM %s WHERE k = \"TOKEN\"(2) AND v IS NOT NULL" +
" PRIMARY KEY (v, k)",
"k = \"TOKEN\"(2) AND v IS NOT NULL",
"INSERT INTO %s(k, v) VALUES (0, 1)",
"INSERT INTO %s(k, v) VALUES (2, 3)"), row(3, 2));
// UDF with lowercase name conflicting with both TOKEN keyword and native token function name,
// requires specifying the keyspace and should be quoted in the schema where clause
assertRows(testFunctionInWhereClause("CREATE TABLE %s (k int PRIMARY KEY, v int)",
"CREATE FUNCTION \"token\"(x int)" +
" CALLED ON NULL INPUT" +
" RETURNS int" +
" LANGUAGE java" +
" AS 'return x;'",
"CREATE MATERIALIZED VIEW %s AS" +
" SELECT * FROM %s " +
" WHERE k = " + keyspace() + ".\"token\"(2) AND v IS NOT NULL" +
" PRIMARY KEY (v, k)",
"k = " + keyspace() + ".\"token\"(2) AND v IS NOT NULL",
"INSERT INTO %s(k, v) VALUES (0, 1)",
"INSERT INTO %s(k, v) VALUES (2, 3)"), row(3, 2));
}
/**
* Tests that truncating a table stops the ongoing builds of its materialized views,
* so they don't write into the MV data that has been truncated in the base table.
* <p>
* See CASSANDRA-16567 for further details.
*/
@Test
@BMRules(rules = {
@BMRule(name = "Block view builder tasks",
targetClass = "ViewBuilderTask",
targetMethod = "buildKey",
action = "com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly" +
"(org.apache.cassandra.cql3.ViewTest.blockViewBuild);"),
@BMRule(name = "Unblock view builder tasks",
targetClass = "ColumnFamilyStore",
targetMethod = "truncateBlocking",
action = "org.apache.cassandra.cql3.ViewTest.blockViewBuild.countDown();")
})
public void testTruncateWhileBuilding() throws Throwable
{
createTable("CREATE TABLE %s (k int, c int, v int, PRIMARY KEY(k, c))");
execute("INSERT INTO %s (k, c, v) VALUES (?, ?, ?)", 0, 0, 0);
createViewAsync("CREATE MATERIALIZED VIEW %s AS SELECT * FROM %s " +
"WHERE k IS NOT NULL AND c IS NOT NULL AND v IS NOT NULL " +
"PRIMARY KEY (v, c, k)");
// check that the delayed view builder tasks are either running or pending,
// and that they haven't written anything yet
assertThat(runningViewBuilds()).isPositive();
assertFalse(SystemKeyspace.isViewBuilt(KEYSPACE, currentView()));
waitForViewMutations();
assertRows(executeView("SELECT * FROM %s"));
// truncate the view, this should unblock the view builders, wait for their cancellation,
// drop the sstables and, finally, start a new view build
updateView("TRUNCATE %s");
// check that there aren't any rows after truncating
assertRows(executeView("SELECT * FROM %s"));
// check that the view builder tasks finish and that the view is still empty after that
Awaitility.await().untilAsserted(() -> assertEquals(0, runningViewBuilds()));
assertTrue(SystemKeyspace.isViewBuilt(KEYSPACE, currentView()));
waitForViewMutations();
assertRows(executeView("SELECT * FROM %s"));
}
private static int runningViewBuilds()
{
return Metrics.getThreadPoolMetrics("ViewBuildExecutor")
.map(p -> p.activeTasks.getValue() + p.pendingTasks.getValue())
.orElse(0);
}
private UntypedResultSet testFunctionInWhereClause(String createTableQuery,
String createFunctionQuery,
String createViewQuery,
String expectedSchemaWhereClause,
String... insertQueries) throws Throwable
{
createTable(createTableQuery);
if (createFunctionQuery != null)
{
execute(createFunctionQuery);
}
createView(createViewQuery);
// Test the where clause stored in system_schema.views
String schemaQuery = String.format("SELECT where_clause FROM %s.%s WHERE keyspace_name = ? AND view_name = ?",
SchemaConstants.SCHEMA_KEYSPACE_NAME,
SchemaKeyspaceTables.VIEWS);
assertRows(execute(schemaQuery, keyspace(), currentView()), row(expectedSchemaWhereClause));
for (String insert : insertQueries)
{
execute(insert);
}
return executeView("SELECT * FROM %s");
}
}