This is an automated email from the ASF dual-hosted git repository. bereng pushed a commit to branch cassandra-4.0 in repository https://gitbox.apache.org/repos/asf/cassandra.git
commit e98be8e3ec7ba68e17ce4b50a9a71b01ebefca1a Merge: 14af149 8f4ae7d Author: Bereng <berenguerbl...@gmail.com> AuthorDate: Mon Sep 20 09:44:02 2021 +0200 Merge branch 'cassandra-3.11' into cassandra-4.0 .../cassandra/cql3/statements/schema/AlterViewStatement.java | 5 +++-- test/unit/org/apache/cassandra/cql3/ViewTimesTest.java | 11 ++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --cc src/java/org/apache/cassandra/cql3/statements/schema/AlterViewStatement.java index 1931bb4,0000000..3493eb0 mode 100644,000000..100644 --- a/src/java/org/apache/cassandra/cql3/statements/schema/AlterViewStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/schema/AlterViewStatement.java @@@ -1,117 -1,0 +1,118 @@@ +/* + * 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.statements.schema; + +import org.apache.cassandra.audit.AuditLogContext; +import org.apache.cassandra.audit.AuditLogEntryType; +import org.apache.cassandra.auth.Permission; +import org.apache.cassandra.cql3.CQLStatement; +import org.apache.cassandra.cql3.QualifiedName; +import org.apache.cassandra.schema.*; +import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff; +import org.apache.cassandra.service.ClientState; +import org.apache.cassandra.transport.Event.SchemaChange; +import org.apache.cassandra.transport.Event.SchemaChange.Change; +import org.apache.cassandra.transport.Event.SchemaChange.Target; + +public final class AlterViewStatement extends AlterSchemaStatement +{ + private final String viewName; + private final TableAttributes attrs; + + public AlterViewStatement(String keyspaceName, String viewName, TableAttributes attrs) + { + super(keyspaceName); + this.viewName = viewName; + this.attrs = attrs; + } + + public Keyspaces apply(Keyspaces schema) + { + KeyspaceMetadata keyspace = schema.getNullable(keyspaceName); + + ViewMetadata view = null == keyspace + ? null + : keyspace.views.getNullable(viewName); + + if (null == view) + throw ire("Materialized view '%s.%s' doesn't exist", keyspaceName, viewName); + + attrs.validate(); + + TableParams params = attrs.asAlteredTableParams(view.metadata.params); + + if (params.gcGraceSeconds == 0) + { + throw ire("Cannot alter gc_grace_seconds of a materialized view to 0, since this " + + "value is used to TTL undelivered updates. Setting gc_grace_seconds too " + + "low might cause undelivered updates to expire before being replayed."); + } + + if (params.defaultTimeToLive > 0) + { - throw ire("Cannot set or alter default_time_to_live for a materialized view. " + ++ throw ire("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."); ++ "the corresponding data in the parent table. default_time_to_live " + ++ "must be set to zero, see CASSANDRA-12868 for more information"); + } + + ViewMetadata newView = view.copy(view.metadata.withSwapped(params)); + return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.views.withSwapped(newView))); + } + + SchemaChange schemaChangeEvent(KeyspacesDiff diff) + { + return new SchemaChange(Change.UPDATED, Target.TABLE, keyspaceName, viewName); + } + + public void authorize(ClientState client) + { + ViewMetadata view = Schema.instance.getView(keyspaceName, viewName); + if (null != view) + client.ensureTablePermission(keyspaceName, view.baseTableName, Permission.ALTER); + } + + @Override + public AuditLogContext getAuditLogContext() + { + return new AuditLogContext(AuditLogEntryType.ALTER_VIEW, keyspaceName, viewName); + } + + public String toString() + { + return String.format("%s (%s, %s)", getClass().getSimpleName(), keyspaceName, viewName); + } + + public static final class Raw extends CQLStatement.Raw + { + private final QualifiedName name; + private final TableAttributes attrs; + + public Raw(QualifiedName name, TableAttributes attrs) + { + this.name = name; + this.attrs = attrs; + } + + public AlterViewStatement prepare(ClientState state) + { + String keyspaceName = name.hasKeyspace() ? name.getKeyspace() : state.getKeyspace(); + return new AlterViewStatement(keyspaceName, name.getName(), attrs); + } + } +} diff --cc test/unit/org/apache/cassandra/cql3/ViewTimesTest.java index 4fee422,0000000..14d7566 mode 100644,000000..100644 --- a/test/unit/org/apache/cassandra/cql3/ViewTimesTest.java +++ b/test/unit/org/apache/cassandra/cql3/ViewTimesTest.java @@@ -1,300 -1,0 +1,309 @@@ +/* + * 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 org.junit.Assert; +import org.junit.Test; + +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import org.apache.cassandra.db.Keyspace; +import org.apache.cassandra.utils.FBUtilities; + ++import static org.junit.Assert.assertEquals; ++ +/* + * 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)"); + + execute("USE " + keyspace()); + executeNet("USE " + keyspace()); + + createView("mv_rctstest", "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(execute("SELECT c, k, val FROM mv_rctstest"), 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(execute("SELECT c, k, val FROM mv_rctstest"), row(2, 0, 2)); + assertRows(execute("SELECT c, k, val FROM mv_rctstest 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))"); + + execute("USE " + keyspace()); + executeNet("USE " + keyspace()); + Keyspace ks = Keyspace.open(keyspace()); + + createView("mv", "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("mv").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(execute("SELECT d from mv 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(execute("SELECT d from mv WHERE c = ? and a = ? and b = ?", 1, 0, 0), row(0)); + + if (flush) + FBUtilities.waitOnFutures(ks.flush()); + + // 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) + FBUtilities.waitOnFutures(ks.flush()); + assertRows(execute("SELECT d from mv WHERE c = ? and a = ? and b = ?", 1, 0, 0)); + + if(flush) + { + ks.getColumnFamilyStore("mv").forceMajorCompaction(); + FBUtilities.waitOnFutures(ks.flush()); + } + + + //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("mv").forceMajorCompaction(); + FBUtilities.waitOnFutures(ks.flush()); + } + + assertRows(execute("SELECT d,e from mv 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(execute("SELECT d,e from mv WHERE c = ? and a = ? and b = ?", 1, 0, 0), row(0, 1)); + + if (flush) + FBUtilities.waitOnFutures(ks.flush()); + + + //Change d value @ TS=2 + executeNet("UPDATE %s USING TIMESTAMP 2 SET d = ? WHERE a = ? and b = ? ", 2, 0, 0); + assertRows(execute("SELECT d from mv WHERE c = ? and a = ? and b = ?", 1, 0, 0), row(2)); + + if (flush) + FBUtilities.waitOnFutures(ks.flush()); + + + //Change d value @ TS=3 + executeNet("UPDATE %s USING TIMESTAMP 3 SET d = ? WHERE a = ? and b = ? ", 1, 0, 0); + assertRows(execute("SELECT d from mv WHERE c = ? and a = ? and b = ?", 1, 0, 0), row(1)); + + + //Tombstone c + executeNet("DELETE FROM %s WHERE a = ? and b = ?", 0, 0); + assertRows(execute("SELECT d from mv")); + + //Add back without D + executeNet("INSERT INTO %s (a, b, c) VALUES (0, 0, 1)"); + + //Make sure D doesn't pop back in. + assertRows(execute("SELECT d from mv 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(execute("SELECT a, b, c, d, e from mv 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(execute("SELECT a, b, c, d, e from mv 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(execute("SELECT a, b, c, d, e from mv 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("mv", "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 = executeNet("SELECT d FROM mv 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("mv", "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, executeNet("SELECT * FROM mv 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("mv", "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 = executeNet("SELECT c FROM mv"); + List<Row> rows = executeNet("SELECT c FROM %s").all(); + Assert.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() throws Throwable + { + createTable("CREATE TABLE %s (" + + "k int PRIMARY KEY, " + + "c int, " + + "val int) WITH default_time_to_live = 60"); + + execute("USE " + keyspace()); + executeNet("USE " + keyspace()); + + // Must NOT include "default_time_to_live" for Materialized View creation + try + { + createView("mv_ttl1", "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"); + Assert.fail("Should fail if TTL is provided for materialized view"); + } + catch (Exception e) + { + } + } + + @Test + public void testAlterMvWithTTL() throws Throwable + { + createTable("CREATE TABLE %s (" + + "k int PRIMARY KEY, " + + "c int, " + + "val int) WITH default_time_to_live = 60"); + ++ execute("USE " + keyspace()); ++ executeNet("USE " + keyspace()); ++ + createView("mv_ttl2", "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 %s WITH default_time_to_live = 30"); ++ executeNet("ALTER MATERIALIZED VIEW " + keyspace()+ ".mv_ttl2 WITH default_time_to_live = 30"); + Assert.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()); + } + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org