| /* |
| * 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.operations; |
| |
| import java.util.UUID; |
| |
| import org.junit.Test; |
| |
| import org.apache.cassandra.config.DatabaseDescriptor; |
| import org.apache.cassandra.cql3.CQLTester; |
| import org.apache.cassandra.db.ColumnFamilyStore; |
| import org.apache.cassandra.db.Keyspace; |
| import org.apache.cassandra.dht.OrderPreservingPartitioner; |
| import org.apache.cassandra.exceptions.ConfigurationException; |
| import org.apache.cassandra.exceptions.SyntaxException; |
| import org.apache.cassandra.locator.InetAddressAndPort; |
| import org.apache.cassandra.locator.TokenMetadata; |
| import org.apache.cassandra.schema.SchemaConstants; |
| import org.apache.cassandra.schema.SchemaKeyspaceTables; |
| import org.apache.cassandra.service.StorageService; |
| import org.apache.cassandra.utils.FBUtilities; |
| |
| import static java.lang.String.format; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| public class AlterTest extends CQLTester |
| { |
| @Test |
| public void testAddList() throws Throwable |
| { |
| createTable("CREATE TABLE %s (id text PRIMARY KEY, content text);"); |
| alterTable("ALTER TABLE %s ADD myCollection list<text>;"); |
| execute("INSERT INTO %s (id, content , myCollection) VALUES ('test', 'first test', ['first element']);"); |
| |
| assertRows(execute("SELECT * FROM %s;"), row("test", "first test", list("first element"))); |
| } |
| |
| @Test |
| public void testDropList() throws Throwable |
| { |
| createTable("CREATE TABLE %s (id text PRIMARY KEY, content text, myCollection list<text>);"); |
| execute("INSERT INTO %s (id, content , myCollection) VALUES ('test', 'first test', ['first element']);"); |
| alterTable("ALTER TABLE %s DROP myCollection;"); |
| |
| assertRows(execute("SELECT * FROM %s;"), row("test", "first test")); |
| } |
| |
| @Test |
| public void testAddMap() throws Throwable |
| { |
| createTable("CREATE TABLE %s (id text PRIMARY KEY, content text);"); |
| alterTable("ALTER TABLE %s ADD myCollection map<text, text>;"); |
| execute("INSERT INTO %s (id, content , myCollection) VALUES ('test', 'first test', { '1' : 'first element'});"); |
| |
| assertRows(execute("SELECT * FROM %s;"), row("test", "first test", map("1", "first element"))); |
| } |
| |
| @Test |
| public void testDropMap() throws Throwable |
| { |
| createTable("CREATE TABLE %s (id text PRIMARY KEY, content text, myCollection map<text, text>);"); |
| execute("INSERT INTO %s (id, content , myCollection) VALUES ('test', 'first test', { '1' : 'first element'});"); |
| alterTable("ALTER TABLE %s DROP myCollection;"); |
| |
| assertRows(execute("SELECT * FROM %s;"), row("test", "first test")); |
| } |
| |
| @Test |
| public void testDropListAndAddListWithSameName() throws Throwable |
| { |
| createTable("CREATE TABLE %s (id text PRIMARY KEY, content text, myCollection list<text>);"); |
| execute("INSERT INTO %s (id, content , myCollection) VALUES ('test', 'first test', ['first element']);"); |
| alterTable("ALTER TABLE %s DROP myCollection;"); |
| alterTable("ALTER TABLE %s ADD myCollection list<text>;"); |
| assertRows(execute("SELECT * FROM %s;"), row("test", "first test", null)); |
| execute("UPDATE %s set myCollection = ['second element'] WHERE id = 'test';"); |
| assertRows(execute("SELECT * FROM %s;"), row("test", "first test", list("second element"))); |
| } |
| |
| @Test |
| public void testDropListAndAddMapWithSameName() throws Throwable |
| { |
| createTable("CREATE TABLE %s (id text PRIMARY KEY, content text, myCollection list<text>);"); |
| execute("INSERT INTO %s (id, content , myCollection) VALUES ('test', 'first test', ['first element']);"); |
| alterTable("ALTER TABLE %s DROP myCollection;"); |
| |
| assertInvalid("ALTER TABLE %s ADD myCollection map<int, int>;"); |
| } |
| |
| @Test |
| public void testDropWithTimestamp() throws Throwable |
| { |
| createTable("CREATE TABLE %s (id int, c1 int, v1 int, todrop int, PRIMARY KEY (id, c1));"); |
| for (int i = 0; i < 5; i++) |
| execute("INSERT INTO %s (id, c1, v1, todrop) VALUES (?, ?, ?, ?) USING TIMESTAMP ?", 1, i, i, i, 10000L * i); |
| |
| // flush is necessary since otherwise the values of `todrop` will get discarded during |
| // alter statement |
| flush(true); |
| alterTable("ALTER TABLE %s DROP todrop USING TIMESTAMP 20000;"); |
| alterTable("ALTER TABLE %s ADD todrop int;"); |
| execute("INSERT INTO %s (id, c1, v1, todrop) VALUES (?, ?, ?, ?) USING TIMESTAMP ?", 1, 100, 100, 100, 30000L); |
| assertRows(execute("SELECT id, c1, v1, todrop FROM %s"), |
| row(1, 0, 0, null), |
| row(1, 1, 1, null), |
| row(1, 2, 2, null), |
| row(1, 3, 3, 3), |
| row(1, 4, 4, 4), |
| row(1, 100, 100, 100)); |
| } |
| |
| @Test |
| public void testDropAddWithDifferentKind() throws Throwable |
| { |
| createTable("CREATE TABLE %s (a int, b int, c int, d int static, PRIMARY KEY (a, b));"); |
| |
| alterTable("ALTER TABLE %s DROP c;"); |
| alterTable("ALTER TABLE %s DROP d;"); |
| |
| assertInvalidMessage("Cannot re-add previously dropped column 'c' of kind STATIC, incompatible with previous kind REGULAR", |
| "ALTER TABLE %s ADD c int static;"); |
| |
| assertInvalidMessage("Cannot re-add previously dropped column 'd' of kind REGULAR, incompatible with previous kind STATIC", |
| "ALTER TABLE %s ADD d int;"); |
| } |
| |
| @Test |
| public void testDropStaticWithTimestamp() throws Throwable |
| { |
| createTable("CREATE TABLE %s (id int, c1 int, v1 int, todrop int static, PRIMARY KEY (id, c1));"); |
| for (int i = 0; i < 5; i++) |
| execute("INSERT INTO %s (id, c1, v1, todrop) VALUES (?, ?, ?, ?) USING TIMESTAMP ?", 1, i, i, i, 10000L * i); |
| |
| // flush is necessary since otherwise the values of `todrop` will get discarded during |
| // alter statement |
| flush(true); |
| alterTable("ALTER TABLE %s DROP todrop USING TIMESTAMP 20000;"); |
| alterTable("ALTER TABLE %s ADD todrop int static;"); |
| execute("INSERT INTO %s (id, c1, v1, todrop) VALUES (?, ?, ?, ?) USING TIMESTAMP ?", 1, 100, 100, 100, 30000L); |
| // static column value with largest timestmap will be available again |
| assertRows(execute("SELECT id, c1, v1, todrop FROM %s"), |
| row(1, 0, 0, 4), |
| row(1, 1, 1, 4), |
| row(1, 2, 2, 4), |
| row(1, 3, 3, 4), |
| row(1, 4, 4, 4), |
| row(1, 100, 100, 4)); |
| } |
| |
| @Test |
| public void testDropMultipleWithTimestamp() throws Throwable |
| { |
| createTable("CREATE TABLE %s (id int, c1 int, v1 int, todrop1 int, todrop2 int, PRIMARY KEY (id, c1));"); |
| for (int i = 0; i < 5; i++) |
| execute("INSERT INTO %s (id, c1, v1, todrop1, todrop2) VALUES (?, ?, ?, ?, ?) USING TIMESTAMP ?", 1, i, i, i, i, 10000L * i); |
| |
| // flush is necessary since otherwise the values of `todrop1` and `todrop2` will get discarded during |
| // alter statement |
| flush(true); |
| alterTable("ALTER TABLE %s DROP (todrop1, todrop2) USING TIMESTAMP 20000;"); |
| alterTable("ALTER TABLE %s ADD todrop1 int;"); |
| alterTable("ALTER TABLE %s ADD todrop2 int;"); |
| |
| execute("INSERT INTO %s (id, c1, v1, todrop1, todrop2) VALUES (?, ?, ?, ?, ?) USING TIMESTAMP ?", 1, 100, 100, 100, 100, 40000L); |
| assertRows(execute("SELECT id, c1, v1, todrop1, todrop2 FROM %s"), |
| row(1, 0, 0, null, null), |
| row(1, 1, 1, null, null), |
| row(1, 2, 2, null, null), |
| row(1, 3, 3, 3, 3), |
| row(1, 4, 4, 4, 4), |
| row(1, 100, 100, 100, 100)); |
| } |
| |
| |
| @Test |
| public void testChangeStrategyWithUnquotedAgrument() throws Throwable |
| { |
| createTable("CREATE TABLE %s (id text PRIMARY KEY);"); |
| |
| assertInvalidSyntaxMessage("no viable alternative at input '}'", |
| "ALTER TABLE %s WITH caching = {'keys' : 'all', 'rows_per_partition' : ALL};"); |
| } |
| |
| @Test |
| // tests CASSANDRA-7976 |
| public void testAlterIndexInterval() throws Throwable |
| { |
| String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (id uuid, album text, artist text, data blob, PRIMARY KEY (id))"); |
| ColumnFamilyStore cfs = Keyspace.open(KEYSPACE).getColumnFamilyStore(tableName); |
| |
| alterTable("ALTER TABLE %s WITH min_index_interval=256 AND max_index_interval=512"); |
| assertEquals(256, cfs.metadata().params.minIndexInterval); |
| assertEquals(512, cfs.metadata().params.maxIndexInterval); |
| |
| alterTable("ALTER TABLE %s WITH caching = {}"); |
| assertEquals(256, cfs.metadata().params.minIndexInterval); |
| assertEquals(512, cfs.metadata().params.maxIndexInterval); |
| } |
| |
| /** |
| * Migrated from cql_tests.py:TestCQL.create_alter_options_test() |
| */ |
| @Test |
| public void testCreateAlterKeyspaces() throws Throwable |
| { |
| assertInvalidThrow(SyntaxException.class, "CREATE KEYSPACE ks1"); |
| assertInvalidThrow(ConfigurationException.class, "CREATE KEYSPACE ks1 WITH replication= { 'replication_factor' : 1 }"); |
| |
| String ks1 = createKeyspace("CREATE KEYSPACE %s WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }"); |
| String ks2 = createKeyspace("CREATE KEYSPACE %s WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 } AND durable_writes=false"); |
| |
| assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes FROM system_schema.keyspaces"), |
| row(KEYSPACE, true), |
| row(KEYSPACE_PER_TEST, true), |
| row(ks1, true), |
| row(ks2, false)); |
| |
| schemaChange("ALTER KEYSPACE " + ks1 + " WITH replication = { 'class' : 'NetworkTopologyStrategy', '" + DATA_CENTER + "' : 1 } AND durable_writes=False"); |
| schemaChange("ALTER KEYSPACE " + ks2 + " WITH durable_writes=true"); |
| |
| assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"), |
| row(KEYSPACE, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(KEYSPACE_PER_TEST, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(ks1, false, map("class", "org.apache.cassandra.locator.NetworkTopologyStrategy", DATA_CENTER, "1")), |
| row(ks2, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1"))); |
| |
| execute("USE " + ks1); |
| |
| assertInvalidThrow(ConfigurationException.class, "CREATE TABLE cf1 (a int PRIMARY KEY, b int) WITH compaction = { 'min_threshold' : 4 }"); |
| |
| execute("CREATE TABLE cf1 (a int PRIMARY KEY, b int) WITH compaction = { 'class' : 'SizeTieredCompactionStrategy', 'min_threshold' : 7 }"); |
| assertRows(execute("SELECT table_name, compaction FROM system_schema.tables WHERE keyspace_name='" + ks1 + "'"), |
| row("cf1", map("class", "org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy", |
| "min_threshold", "7", |
| "max_threshold", "32"))); |
| } |
| |
| @Test |
| public void testCreateAlterNetworkTopologyWithDefaults() throws Throwable |
| { |
| TokenMetadata metadata = StorageService.instance.getTokenMetadata(); |
| metadata.clearUnsafe(); |
| InetAddressAndPort local = FBUtilities.getBroadcastAddressAndPort(); |
| InetAddressAndPort remote = InetAddressAndPort.getByName("127.0.0.4"); |
| metadata.updateHostId(UUID.randomUUID(), local); |
| metadata.updateNormalToken(new OrderPreservingPartitioner.StringToken("A"), local); |
| metadata.updateHostId(UUID.randomUUID(), remote); |
| metadata.updateNormalToken(new OrderPreservingPartitioner.StringToken("B"), remote); |
| |
| // With two datacenters we should respect anything passed in as a manual override |
| String ks1 = createKeyspace("CREATE KEYSPACE %s WITH replication={ 'class' : 'NetworkTopologyStrategy', 'replication_factor' : 1, '" + DATA_CENTER_REMOTE + "': 3}"); |
| |
| assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"), |
| row(KEYSPACE, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(KEYSPACE_PER_TEST, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(ks1, true, map("class", "org.apache.cassandra.locator.NetworkTopologyStrategy", DATA_CENTER, "1", DATA_CENTER_REMOTE, "3"))); |
| |
| // Should be able to remove data centers |
| schemaChange("ALTER KEYSPACE " + ks1 + " WITH replication = { 'class' : 'NetworkTopologyStrategy', '" + DATA_CENTER + "' : 0, '" + DATA_CENTER_REMOTE + "': 3 }"); |
| |
| assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"), |
| row(KEYSPACE, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(KEYSPACE_PER_TEST, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(ks1, true, map("class", "org.apache.cassandra.locator.NetworkTopologyStrategy", DATA_CENTER_REMOTE, "3"))); |
| |
| // The auto-expansion should not change existing replication counts; do not let the user shoot themselves in the foot |
| schemaChange("ALTER KEYSPACE " + ks1 + " WITH replication = { 'class' : 'NetworkTopologyStrategy', 'replication_factor' : 1 } AND durable_writes=True"); |
| |
| assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"), |
| row(KEYSPACE, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(KEYSPACE_PER_TEST, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(ks1, true, map("class", "org.apache.cassandra.locator.NetworkTopologyStrategy", DATA_CENTER, "1", DATA_CENTER_REMOTE, "3"))); |
| |
| // The keyspace should be fully functional |
| execute("USE " + ks1); |
| |
| assertInvalidThrow(ConfigurationException.class, "CREATE TABLE tbl1 (a int PRIMARY KEY, b int) WITH compaction = { 'min_threshold' : 4 }"); |
| |
| execute("CREATE TABLE tbl1 (a int PRIMARY KEY, b int) WITH compaction = { 'class' : 'SizeTieredCompactionStrategy', 'min_threshold' : 7 }"); |
| |
| assertRows(execute("SELECT table_name, compaction FROM system_schema.tables WHERE keyspace_name='" + ks1 + "'"), |
| row("tbl1", map("class", "org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy", |
| "min_threshold", "7", |
| "max_threshold", "32"))); |
| metadata.clearUnsafe(); |
| } |
| |
| @Test |
| public void testCreateSimpleAlterNTSDefaults() throws Throwable |
| { |
| TokenMetadata metadata = StorageService.instance.getTokenMetadata(); |
| metadata.clearUnsafe(); |
| InetAddressAndPort local = FBUtilities.getBroadcastAddressAndPort(); |
| InetAddressAndPort remote = InetAddressAndPort.getByName("127.0.0.4"); |
| metadata.updateHostId(UUID.randomUUID(), local); |
| metadata.updateNormalToken(new OrderPreservingPartitioner.StringToken("A"), local); |
| metadata.updateHostId(UUID.randomUUID(), remote); |
| metadata.updateNormalToken(new OrderPreservingPartitioner.StringToken("B"), remote); |
| |
| // Let's create a keyspace first with SimpleStrategy |
| String ks1 = createKeyspace("CREATE KEYSPACE %s WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 2}"); |
| |
| assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"), |
| row(KEYSPACE, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(KEYSPACE_PER_TEST, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(ks1, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "2"))); |
| |
| // Now we should be able to ALTER to NetworkTopologyStrategy directly from SimpleStrategy without supplying replication_factor |
| schemaChange("ALTER KEYSPACE " + ks1 + " WITH replication = { 'class' : 'NetworkTopologyStrategy'}"); |
| |
| assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"), |
| row(KEYSPACE, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(KEYSPACE_PER_TEST, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(ks1, true, map("class", "org.apache.cassandra.locator.NetworkTopologyStrategy", DATA_CENTER, "2", DATA_CENTER_REMOTE, "2"))); |
| |
| schemaChange("ALTER KEYSPACE " + ks1 + " WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 3}"); |
| schemaChange("ALTER KEYSPACE " + ks1 + " WITH replication = { 'class' : 'NetworkTopologyStrategy', 'replication_factor': 2}"); |
| |
| assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"), |
| row(KEYSPACE, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(KEYSPACE_PER_TEST, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")), |
| row(ks1, true, map("class", "org.apache.cassandra.locator.NetworkTopologyStrategy", DATA_CENTER, "2", DATA_CENTER_REMOTE, "2"))); |
| } |
| |
| /** |
| * Test {@link ConfigurationException} thrown on alter keyspace to no DC option in replication configuration. |
| */ |
| @Test |
| public void testAlterKeyspaceWithNoOptionThrowsConfigurationException() throws Throwable |
| { |
| // Create keyspaces |
| execute("CREATE KEYSPACE testABC WITH replication={ 'class' : 'NetworkTopologyStrategy', '" + DATA_CENTER + "' : 3 }"); |
| execute("CREATE KEYSPACE testXYZ WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 3 }"); |
| |
| // Try to alter the created keyspace without any option |
| assertInvalidThrow(ConfigurationException.class, "ALTER KEYSPACE testABC WITH replication={ 'class' : 'NetworkTopologyStrategy' }"); |
| assertInvalidThrow(ConfigurationException.class, "ALTER KEYSPACE testXYZ WITH replication={ 'class' : 'SimpleStrategy' }"); |
| |
| // Make sure that the alter works as expected |
| alterTable("ALTER KEYSPACE testABC WITH replication={ 'class' : 'NetworkTopologyStrategy', '" + DATA_CENTER + "' : 2 }"); |
| alterTable("ALTER KEYSPACE testXYZ WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 2 }"); |
| |
| // clean up |
| execute("DROP KEYSPACE IF EXISTS testABC"); |
| execute("DROP KEYSPACE IF EXISTS testXYZ"); |
| } |
| |
| /** |
| * Test {@link ConfigurationException} thrown when altering a keyspace to invalid DC option in replication configuration. |
| */ |
| @Test |
| public void testAlterKeyspaceWithNTSOnlyAcceptsConfiguredDataCenterNames() throws Throwable |
| { |
| // Create a keyspace with expected DC name. |
| createKeyspace("CREATE KEYSPACE %s WITH replication = {'class' : 'NetworkTopologyStrategy', '" + DATA_CENTER + "' : 2 }"); |
| |
| // try modifying the keyspace |
| assertAlterKeyspaceThrowsException(ConfigurationException.class, |
| "Unrecognized strategy option {INVALID_DC} passed to NetworkTopologyStrategy for keyspace " + currentKeyspace(), |
| "ALTER KEYSPACE %s WITH replication = { 'class' : 'NetworkTopologyStrategy', 'INVALID_DC' : 2 }"); |
| |
| alterKeyspace("ALTER KEYSPACE %s WITH replication = {'class' : 'NetworkTopologyStrategy', '" + DATA_CENTER + "' : 3 }"); |
| |
| // Mix valid and invalid, should throw an exception |
| assertAlterKeyspaceThrowsException(ConfigurationException.class, |
| "Unrecognized strategy option {INVALID_DC} passed to NetworkTopologyStrategy for keyspace " + currentKeyspace(), |
| "ALTER KEYSPACE %s WITH replication={ 'class' : 'NetworkTopologyStrategy', '" + DATA_CENTER + "' : 2 , 'INVALID_DC': 1}"); |
| } |
| |
| @Test |
| public void testAlterKeyspaceWithMultipleInstancesOfSameDCThrowsSyntaxException() throws Throwable |
| { |
| // Create a keyspace |
| createKeyspace("CREATE KEYSPACE %s WITH replication = {'class' : 'NetworkTopologyStrategy', '" + DATA_CENTER + "' : 2}"); |
| |
| // try modifying the keyspace |
| assertAlterTableThrowsException(SyntaxException.class, |
| "", |
| "ALTER KEYSPACE %s WITH replication = {'class' : 'NetworkTopologyStrategy', '" + DATA_CENTER + "' : 2, '" + DATA_CENTER + "' : 3 }"); |
| alterKeyspace("ALTER KEYSPACE %s WITH replication = {'class' : 'NetworkTopologyStrategy', '" + DATA_CENTER + "' : 3}"); |
| } |
| |
| /** |
| * Test for bug of 5232, |
| * migrated from cql_tests.py:TestCQL.alter_bug_test() |
| */ |
| @Test |
| public void testAlterStatementWithAdd() throws Throwable |
| { |
| createTable("CREATE TABLE %s (id int PRIMARY KEY, t text)"); |
| |
| execute("UPDATE %s SET t = '111' WHERE id = 1"); |
| |
| alterTable("ALTER TABLE %s ADD l list<text>"); |
| assertRows(execute("SELECT * FROM %s"), |
| row(1, null, "111")); |
| |
| alterTable("ALTER TABLE %s ADD m map<int, text>"); |
| assertRows(execute("SELECT * FROM %s"), |
| row(1, null, null, "111")); |
| } |
| |
| /** |
| * Test for 7744, |
| * migrated from cql_tests.py:TestCQL.downgrade_to_compact_bug_test() |
| */ |
| @Test |
| public void testDowngradeToCompact() throws Throwable |
| { |
| createTable("create table %s (k int primary key, v set<text>)"); |
| execute("insert into %s (k, v) VALUES (0, {'f'})"); |
| flush(); |
| alterTable("alter table %s drop v"); |
| alterTable("alter table %s add v1 int"); |
| } |
| |
| @Test |
| // tests CASSANDRA-9565 |
| public void testDoubleWith() throws Throwable |
| { |
| String[] stmts = { "ALTER KEYSPACE WITH WITH DURABLE_WRITES = true", |
| "ALTER KEYSPACE ks WITH WITH DURABLE_WRITES = true" }; |
| |
| for (String stmt : stmts) { |
| assertAlterTableThrowsException(SyntaxException.class, "no viable alternative at input 'WITH'", stmt); |
| } |
| } |
| |
| @Test |
| public void testAlterTableWithCompression() throws Throwable |
| { |
| createTable("CREATE TABLE %s (a text, b int, c int, primary key (a, b))"); |
| |
| assertRows(execute(format("SELECT compression FROM %s.%s WHERE keyspace_name = ? and table_name = ?;", |
| SchemaConstants.SCHEMA_KEYSPACE_NAME, |
| SchemaKeyspaceTables.TABLES), |
| KEYSPACE, |
| currentTable()), |
| row(map("chunk_length_in_kb", "16", "class", "org.apache.cassandra.io.compress.LZ4Compressor"))); |
| |
| alterTable("ALTER TABLE %s WITH compression = { 'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 32 };"); |
| |
| assertRows(execute(format("SELECT compression FROM %s.%s WHERE keyspace_name = ? and table_name = ?;", |
| SchemaConstants.SCHEMA_KEYSPACE_NAME, |
| SchemaKeyspaceTables.TABLES), |
| KEYSPACE, |
| currentTable()), |
| row(map("chunk_length_in_kb", "32", "class", "org.apache.cassandra.io.compress.SnappyCompressor"))); |
| |
| alterTable("ALTER TABLE %s WITH compression = { 'class' : 'LZ4Compressor', 'chunk_length_in_kb' : 64 };"); |
| |
| assertRows(execute(format("SELECT compression FROM %s.%s WHERE keyspace_name = ? and table_name = ?;", |
| SchemaConstants.SCHEMA_KEYSPACE_NAME, |
| SchemaKeyspaceTables.TABLES), |
| KEYSPACE, |
| currentTable()), |
| row(map("chunk_length_in_kb", "64", "class", "org.apache.cassandra.io.compress.LZ4Compressor"))); |
| |
| alterTable("ALTER TABLE %s WITH compression = { 'class' : 'LZ4Compressor', 'min_compress_ratio' : 2 };"); |
| |
| assertRows(execute(format("SELECT compression FROM %s.%s WHERE keyspace_name = ? and table_name = ?;", |
| SchemaConstants.SCHEMA_KEYSPACE_NAME, |
| SchemaKeyspaceTables.TABLES), |
| KEYSPACE, |
| currentTable()), |
| row(map("chunk_length_in_kb", "16", "class", "org.apache.cassandra.io.compress.LZ4Compressor", "min_compress_ratio", "2.0"))); |
| |
| alterTable("ALTER TABLE %s WITH compression = { 'class' : 'LZ4Compressor', 'min_compress_ratio' : 1 };"); |
| |
| assertRows(execute(format("SELECT compression FROM %s.%s WHERE keyspace_name = ? and table_name = ?;", |
| SchemaConstants.SCHEMA_KEYSPACE_NAME, |
| SchemaKeyspaceTables.TABLES), |
| KEYSPACE, |
| currentTable()), |
| row(map("chunk_length_in_kb", "16", "class", "org.apache.cassandra.io.compress.LZ4Compressor", "min_compress_ratio", "1.0"))); |
| |
| alterTable("ALTER TABLE %s WITH compression = { 'class' : 'LZ4Compressor', 'min_compress_ratio' : 0 };"); |
| |
| assertRows(execute(format("SELECT compression FROM %s.%s WHERE keyspace_name = ? and table_name = ?;", |
| SchemaConstants.SCHEMA_KEYSPACE_NAME, |
| SchemaKeyspaceTables.TABLES), |
| KEYSPACE, |
| currentTable()), |
| row(map("chunk_length_in_kb", "16", "class", "org.apache.cassandra.io.compress.LZ4Compressor"))); |
| |
| alterTable("ALTER TABLE %s WITH compression = { 'class' : 'SnappyCompressor', 'chunk_length_in_kb' : 32 };"); |
| alterTable("ALTER TABLE %s WITH compression = { 'enabled' : 'false'};"); |
| |
| assertRows(execute(format("SELECT compression FROM %s.%s WHERE keyspace_name = ? and table_name = ?;", |
| SchemaConstants.SCHEMA_KEYSPACE_NAME, |
| SchemaKeyspaceTables.TABLES), |
| KEYSPACE, |
| currentTable()), |
| row(map("enabled", "false"))); |
| |
| assertAlterTableThrowsException(ConfigurationException.class, |
| "Missing sub-option 'class' for the 'compression' option.", |
| "ALTER TABLE %s WITH compression = {'chunk_length_in_kb' : 32};"); |
| |
| assertAlterTableThrowsException(ConfigurationException.class, |
| "The 'class' option must not be empty. To disable compression use 'enabled' : false", |
| "ALTER TABLE %s WITH compression = { 'class' : ''};"); |
| |
| assertAlterTableThrowsException(ConfigurationException.class, |
| "If the 'enabled' option is set to false no other options must be specified", |
| "ALTER TABLE %s WITH compression = { 'enabled' : 'false', 'class' : 'SnappyCompressor'};"); |
| |
| assertAlterTableThrowsException(ConfigurationException.class, |
| "The 'sstable_compression' option must not be used if the compression algorithm is already specified by the 'class' option", |
| "ALTER TABLE %s WITH compression = { 'sstable_compression' : 'SnappyCompressor', 'class' : 'SnappyCompressor'};"); |
| |
| assertAlterTableThrowsException(ConfigurationException.class, |
| "The 'chunk_length_kb' option must not be used if the chunk length is already specified by the 'chunk_length_in_kb' option", |
| "ALTER TABLE %s WITH compression = { 'class' : 'SnappyCompressor', 'chunk_length_kb' : 32 , 'chunk_length_in_kb' : 32 };"); |
| |
| assertAlterTableThrowsException(ConfigurationException.class, |
| "Invalid negative min_compress_ratio", |
| "ALTER TABLE %s WITH compression = { 'class' : 'SnappyCompressor', 'min_compress_ratio' : -1 };"); |
| |
| assertAlterTableThrowsException(ConfigurationException.class, |
| "min_compress_ratio can either be 0 or greater than or equal to 1", |
| "ALTER TABLE %s WITH compression = { 'class' : 'SnappyCompressor', 'min_compress_ratio' : 0.5 };"); |
| } |
| |
| private void assertAlterKeyspaceThrowsException(Class<? extends Throwable> clazz, String msg, String stmt) |
| { |
| assertThrowsException(clazz, msg, () -> {alterKeyspaceMayThrow(stmt);}); |
| } |
| |
| private void assertAlterTableThrowsException(Class<? extends Throwable> clazz, String msg, String stmt) |
| { |
| assertThrowsException(clazz, msg, () -> {alterTableMayThrow(stmt);}); |
| } |
| |
| private static void assertThrowsException(Class<? extends Throwable> clazz, String msg, CheckedFunction function) |
| { |
| try |
| { |
| function.apply(); |
| fail("An error should havee been thrown but was not."); |
| } |
| catch (Throwable e) |
| { |
| assertTrue("Unexpected exception type (expected: " + clazz + ", value: " + e.getClass() + ")", |
| clazz.isAssignableFrom(e.getClass())); |
| assertTrue("Expecting the error message to contains: '" + msg + "' but was " + e.getMessage(), e.getMessage().contains(msg)); |
| } |
| } |
| |
| /** |
| * Test for CASSANDRA-13337. Checks that dropping a column when a sstable contains only data for that column |
| * works properly. |
| */ |
| @Test |
| public void testAlterDropEmptySSTable() throws Throwable |
| { |
| createTable("CREATE TABLE %s(k int PRIMARY KEY, x int, y int)"); |
| |
| execute("UPDATE %s SET x = 1 WHERE k = 0"); |
| |
| flush(); |
| |
| execute("UPDATE %s SET x = 1, y = 1 WHERE k = 0"); |
| |
| flush(); |
| |
| alterTable("ALTER TABLE %s DROP x"); |
| |
| compact(); |
| |
| assertRows(execute("SELECT * FROM %s"), row(0, 1)); |
| } |
| |
| /** |
| * Similarly to testAlterDropEmptySSTable, checks we don't return empty rows from queries (testAlterDropEmptySSTable |
| * tests the compaction case). |
| */ |
| @Test |
| public void testAlterOnlyColumnBehaviorWithFlush() throws Throwable |
| { |
| testAlterOnlyColumnBehaviorWithFlush(true); |
| testAlterOnlyColumnBehaviorWithFlush(false); |
| } |
| |
| private void testAlterOnlyColumnBehaviorWithFlush(boolean flushAfterInsert) throws Throwable |
| { |
| createTable("CREATE TABLE %s(k int PRIMARY KEY, x int, y int)"); |
| |
| execute("UPDATE %s SET x = 1 WHERE k = 0"); |
| |
| assertRows(execute("SELECT * FROM %s"), row(0, 1, null)); |
| |
| if (flushAfterInsert) |
| flush(); |
| |
| alterTable("ALTER TABLE %s DROP x"); |
| |
| assertEmpty(execute("SELECT * FROM %s")); |
| } |
| |
| @Test |
| public void testAlterTypeUsedInPartitionKey() throws Throwable |
| { |
| // frozen UDT used directly in a partition key |
| String type1 = createType("CREATE TYPE %s (v1 int)"); |
| String table1 = createTable("CREATE TABLE %s (pk frozen<" + type1 + ">, val int, PRIMARY KEY(pk));"); |
| |
| // frozen UDT used in a frozen UDT used in a partition key |
| String type2 = createType("CREATE TYPE %s (v1 frozen<" + type1 + ">, v2 frozen<" + type1 + ">)"); |
| String table2 = createTable("CREATE TABLE %s (pk frozen<" + type2 + ">, val int, PRIMARY KEY(pk));"); |
| |
| // frozen UDT used in a frozen collection used in a partition key |
| String table3 = createTable("CREATE TABLE %s (pk frozen<list<frozen<" + type1 + ">>>, val int, PRIMARY KEY(pk));"); |
| |
| // assert that ALTER fails and that the error message contains all the names of the table referencing it |
| assertInvalidMessage(table1, format("ALTER TYPE %s.%s ADD v2 int;", keyspace(), type1)); |
| assertInvalidMessage(table2, format("ALTER TYPE %s.%s ADD v2 int;", keyspace(), type1)); |
| assertInvalidMessage(table3, format("ALTER TYPE %s.%s ADD v2 int;", keyspace(), type1)); |
| } |
| |
| @Test |
| public void testAlterDropCompactStorageDisabled() throws Throwable |
| { |
| DatabaseDescriptor.setEnableDropCompactStorage(false); |
| |
| createTable("CREATE TABLE %s (k text, i int, PRIMARY KEY (k, i)) WITH COMPACT STORAGE"); |
| |
| assertInvalidMessage("DROP COMPACT STORAGE is disabled. Enable in cassandra.yaml to use.", "ALTER TABLE %s DROP COMPACT STORAGE"); |
| } |
| |
| /** |
| * Test for CASSANDRA-14564 |
| */ |
| @Test |
| public void testAlterByAddingColumnToCompactTableShouldFail() throws Throwable |
| { |
| createTable("CREATE TABLE %s (a int, b int, PRIMARY KEY (a, b)) WITH COMPACT STORAGE"); |
| assertInvalidMessage("Cannot add new column to a COMPACT STORAGE table", |
| "ALTER TABLE %s ADD column1 text"); |
| } |
| } |