From f1e3ef48e878cebd698f6bd220f42723ef562045 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 21 Jun 2016 15:52:10 +0200 Subject: [PATCH] DATACASS-192 - Support RENAME and DROP in ALTER TABLE specification. Extend alter table specification and the linked CQL generator to support RENAME and DROP operations. Cleanup code in that area. Original pull request: #69. --- .../cql/generator/AddColumnCqlGenerator.java | 12 +++- .../cql/generator/AlterTableCqlGenerator.java | 43 +++++++++-- .../cql/generator/DropColumnCqlGenerator.java | 4 +- .../generator/RenameColumnCqlGenerator.java | 42 +++++++++++ .../keyspace/AlterTableSpecification.java | 48 ++++++++++--- .../keyspace/RenameColumnSpecification.java | 63 ++++++++++++++++ ...lterTableCqlGeneratorIntegrationTests.java | 71 +++++++++++++++++-- .../AlterTableCqlGeneratorUnitTests.java | 71 +++++++++++++++++++ 8 files changed, 329 insertions(+), 25 deletions(-) create mode 100644 spring-cql/src/main/java/org/springframework/cassandra/core/cql/generator/RenameColumnCqlGenerator.java create mode 100644 spring-cql/src/main/java/org/springframework/cassandra/core/keyspace/RenameColumnSpecification.java diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/cql/generator/AddColumnCqlGenerator.java b/spring-cql/src/main/java/org/springframework/cassandra/core/cql/generator/AddColumnCqlGenerator.java index cf0705956..c4f9c7b46 100644 --- a/spring-cql/src/main/java/org/springframework/cassandra/core/cql/generator/AddColumnCqlGenerator.java +++ b/spring-cql/src/main/java/org/springframework/cassandra/core/cql/generator/AddColumnCqlGenerator.java @@ -15,15 +15,17 @@ */ package org.springframework.cassandra.core.cql.generator; -import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull; +import static org.springframework.cassandra.core.cql.CqlStringUtils.*; import org.springframework.cassandra.core.keyspace.AddColumnSpecification; /** - * CQL generator for generating an ADD clause of an ALTER TABLE statement. + * CQL generator for generating an {@code ADD} clause of an {@code ALTER TABLE} statement. * * @author Matthew T. Adams * @author Mark Paluch + * @see AddColumnSpecification + * @see org.springframework.cassandra.core.keyspace.AlterTableSpecification */ public class AddColumnCqlGenerator extends ColumnChangeCqlGenerator { @@ -31,8 +33,12 @@ public class AddColumnCqlGenerator extends ColumnChangeCqlGeneratorALTER TABLE statements. + * CQL generator for generating {@code ALTER TABLE} statements. * * @author Matthew T. Adams + * @author Mark Paluch + * @see AlterTableSpecification */ public class AlterTableCqlGenerator extends TableOptionsCqlGenerator { + /** + * Generates a CQL statement from the given {@code specification}. + * + * @param specification must not be {@literal null}. + * @return the generated CQL statement. + */ public static String toCql(AlterTableSpecification specification) { return new AlterTableCqlGenerator(specification).toCql(); } + /** + * Creates a new {@literal {@link AlterTableCqlGenerator}. + * + * @param specification must not be {@literal null}. + */ public AlterTableCqlGenerator(AlterTableSpecification specification) { super(specification); } @@ -47,8 +61,16 @@ public class AlterTableCqlGenerator extends TableOptionsCqlGenerator getCqlGeneratorFor(ColumnChangeSpecification change) { + if (change instanceof AddColumnSpecification) { return new AddColumnCqlGenerator((AddColumnSpecification) change); } + if (change instanceof DropColumnSpecification) { return new DropColumnCqlGenerator((DropColumnSpecification) change); } + if (change instanceof AlterColumnSpecification) { return new AlterColumnCqlGenerator((AlterColumnSpecification) change); } + + if (change instanceof RenameColumnSpecification) { + return new RenameColumnCqlGenerator((RenameColumnSpecification) change); + } + throw new IllegalArgumentException("unknown ColumnChangeSpecification type: " + change.getClass().getName()); } @SuppressWarnings("unchecked") protected StringBuilder optionsCql(StringBuilder cql) { + cql = noNull(cql); Map options = spec().getOptions(); @@ -97,7 +128,7 @@ public class AlterTableCqlGenerator extends TableOptionsCqlGeneratorDROP column clause of an ALTER TABLE statement. + * CQL generator for generating a {@code DROP} column clause of an {@code ALTER TABLE} statement. * * @author Matthew T. Adams + * @see DropColumnSpecification + * @see org.springframework.cassandra.core.keyspace.AlterTableSpecification */ public class DropColumnCqlGenerator extends ColumnChangeCqlGenerator { diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/cql/generator/RenameColumnCqlGenerator.java b/spring-cql/src/main/java/org/springframework/cassandra/core/cql/generator/RenameColumnCqlGenerator.java new file mode 100644 index 000000000..2c4fd8806 --- /dev/null +++ b/spring-cql/src/main/java/org/springframework/cassandra/core/cql/generator/RenameColumnCqlGenerator.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed 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.springframework.cassandra.core.cql.generator; + +import static org.springframework.cassandra.core.cql.CqlStringUtils.*; + +import org.springframework.cassandra.core.keyspace.RenameColumnSpecification; + +/** + * CQL generator for generating an {@code RENAME} column clause of an {@code ALTER TABLE} statement. + * + * @author Mark Paluch + * @since 1.5 + * @see RenameColumnSpecification + * @see org.springframework.cassandra.core.keyspace.AlterTableSpecification + */ +public class RenameColumnCqlGenerator extends ColumnChangeCqlGenerator { + + RenameColumnCqlGenerator(RenameColumnSpecification specification) { + super(specification); + } + + /* (non-Javadoc) + * @see org.springframework.cassandra.core.cql.generator.ColumnChangeCqlGenerator#toCql(java.lang.StringBuilder) + */ + public StringBuilder toCql(StringBuilder cql) { + return noNull(cql).append("RENAME ").append(spec().getName()).append(" TO ").append(spec().getTargetName()); + } +} diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/keyspace/AlterTableSpecification.java b/spring-cql/src/main/java/org/springframework/cassandra/core/keyspace/AlterTableSpecification.java index f88b6c3dc..01d63a867 100644 --- a/spring-cql/src/main/java/org/springframework/cassandra/core/keyspace/AlterTableSpecification.java +++ b/spring-cql/src/main/java/org/springframework/cassandra/core/keyspace/AlterTableSpecification.java @@ -24,9 +24,17 @@ import org.springframework.cassandra.core.cql.CqlIdentifier; import com.datastax.driver.core.DataType; /** - * Builder class to construct an ALTER TABLE specification. + * Builder class to construct an {@code ALTER TABLE} specification. * * @author Matthew T. Adams + * @author Mark Paluch + * @see AddColumnSpecification + * @see AlterColumnSpecification + * @see DropColumnSpecification + * @see RenameColumnSpecification + * @see CreateTableSpecification + * @see DropTableSpecification + * @see org.springframework.cassandra.core.cql.generator.AlterTableCqlGenerator */ public class AlterTableSpecification extends TableOptionsSpecification { @@ -60,17 +68,22 @@ public class AlterTableSpecification extends TableOptionsSpecification changes = new ArrayList(); /* - * Adds a DROP to the list of column changes. + * Adds a {@code DROP} to the list of column changes. * - * DW Removed as this only works in C* 2.0 + * @param column must not be empty or {@literal null} + * @return {@literal this} {@link AlterTableSpecification} */ - // public AlterTableSpecification drop(String column) { - // changes.add(new DropColumnSpecification(column)); - // return this; - // } + public AlterTableSpecification drop(String column) { + changes.add(new DropColumnSpecification(column)); + return this; + } /** - * Adds an ADD to the list of column changes. + * Adds an {@code ADD} to the list of column changes. + * + * @param column must not be empty or {@literal null} + * @param type must not be {@literal null} + * @return {@literal this} {@link AlterTableSpecification} */ public AlterTableSpecification add(String column, DataType type) { changes.add(new AddColumnSpecification(column, type)); @@ -78,7 +91,23 @@ public class AlterTableSpecification extends TableOptionsSpecificationALTER to the list of column changes. + * Adds a {@code RENAME} to the list of column changes. + * + * @param from must not be empty or {@literal null} + * @param to must not be empty or {@literal null} + * @return {@literal this} {@link AlterTableSpecification} + */ + public AlterTableSpecification rename(String from, String to) { + changes.add(new RenameColumnSpecification(from, to)); + return this; + } + + /** + * Adds an {@literal ALTER} to the list of column changes. + * + * @param column must not be empty or {@literal null} + * @param type must not be {@literal null} + * @return {@literal this} {@link AlterTableSpecification} */ public AlterTableSpecification alter(String column, DataType type) { changes.add(new AlterColumnSpecification(column, type)); @@ -91,4 +120,5 @@ public class AlterTableSpecification extends TableOptionsSpecification getChanges() { return Collections.unmodifiableList(changes); } + } diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/keyspace/RenameColumnSpecification.java b/spring-cql/src/main/java/org/springframework/cassandra/core/keyspace/RenameColumnSpecification.java new file mode 100644 index 000000000..d1797552c --- /dev/null +++ b/spring-cql/src/main/java/org/springframework/cassandra/core/keyspace/RenameColumnSpecification.java @@ -0,0 +1,63 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed 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.springframework.cassandra.core.keyspace; + +import static org.springframework.cassandra.core.cql.CqlIdentifier.*; + +import org.springframework.cassandra.core.cql.CqlIdentifier; +import org.springframework.util.Assert; + +/** + * {@link ColumnChangeSpecification} to rename a column. + * + * @author Mark Paluch + * @since 1.5 + * @see ColumnChangeSpecification + */ +public class RenameColumnSpecification extends ColumnChangeSpecification { + + private final CqlIdentifier targetName; + + /** + * Creates a new {@link ColumnChangeSpecification}. + * + * @param from must not be empty or {@literal null}. + * @param to must not be empty or {@literal null}. + */ + RenameColumnSpecification(String from, String to) { + this(cqlId(from), cqlId(to)); + } + + /** + * Creates a new {@link ColumnChangeSpecification}. + * + * @param from must not be {@literal null}. + * @param to must not be {@literal null}. + */ + RenameColumnSpecification(CqlIdentifier from, CqlIdentifier to) { + super(from); + + Assert.notNull(to, "Target name must not be null"); + this.targetName = to; + } + + /** + * @return the column name. + */ + public CqlIdentifier getTargetName() { + return targetName; + } +} diff --git a/spring-cql/src/test/java/org/springframework/cassandra/core/cql/generator/AlterTableCqlGeneratorIntegrationTests.java b/spring-cql/src/test/java/org/springframework/cassandra/core/cql/generator/AlterTableCqlGeneratorIntegrationTests.java index d526f38f7..a439a59c7 100644 --- a/spring-cql/src/test/java/org/springframework/cassandra/core/cql/generator/AlterTableCqlGeneratorIntegrationTests.java +++ b/spring-cql/src/test/java/org/springframework/cassandra/core/cql/generator/AlterTableCqlGeneratorIntegrationTests.java @@ -18,9 +18,15 @@ package org.springframework.cassandra.core.cql.generator; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import java.util.LinkedHashMap; +import java.util.Map; + import org.junit.Before; import org.junit.Test; import org.springframework.cassandra.core.keyspace.AlterTableSpecification; +import org.springframework.cassandra.core.keyspace.TableOption; +import org.springframework.cassandra.core.keyspace.TableOption.CachingOption; +import org.springframework.cassandra.core.keyspace.TableOption.KeyCachingOption; import org.springframework.cassandra.test.integration.AbstractKeyspaceCreatingIntegrationTest; import com.datastax.driver.core.ColumnMetadata; @@ -32,6 +38,7 @@ import com.datastax.driver.core.TableMetadata; * Integration tests tests for {@link AlterTableCqlGenerator}. * * @author Mark Paluch + * @see DATACASS-192 */ public class AlterTableCqlGeneratorIntegrationTests extends AbstractKeyspaceCreatingIntegrationTest { @@ -48,8 +55,8 @@ public class AlterTableCqlGeneratorIntegrationTests extends AbstractKeyspaceCrea @Test public void alterTableAlterColumnType() { - session.execute("CREATE TABLE addamsFamily (name varchar PRIMARY KEY, gender varchar,\n" - + " lastknownlocation bigint);"); + session.execute( + "CREATE TABLE addamsFamily (name varchar PRIMARY KEY, gender varchar,\n" + " lastknownlocation bigint);"); AlterTableSpecification spec = AlterTableSpecification.alterTable("addamsFamily").alter("lastKnownLocation", DataType.varint()); @@ -66,8 +73,8 @@ public class AlterTableCqlGeneratorIntegrationTests extends AbstractKeyspaceCrea @Test public void alterTableAlterListColumnType() { - session.execute("CREATE TABLE addamsFamily (name varchar PRIMARY KEY, gender varchar,\n" - + " lastknownlocation list);"); + session.execute( + "CREATE TABLE addamsFamily (name varchar PRIMARY KEY, gender varchar,\n" + " lastknownlocation list);"); AlterTableSpecification spec = AlterTableSpecification.alterTable("addamsFamily").alter("lastKnownLocation", DataType.list(DataType.varchar())); @@ -84,8 +91,8 @@ public class AlterTableCqlGeneratorIntegrationTests extends AbstractKeyspaceCrea @Test public void alterTableAddColumn() { - session.execute("CREATE TABLE addamsFamily (name varchar PRIMARY KEY, gender varchar,\n" - + " lastknownlocation varchar);"); + session.execute( + "CREATE TABLE addamsFamily (name varchar PRIMARY KEY, gender varchar,\n" + " lastknownlocation varchar);"); AlterTableSpecification spec = AlterTableSpecification.alterTable("addamsFamily").add("gravesite", DataType.varchar()); @@ -113,6 +120,58 @@ public class AlterTableCqlGeneratorIntegrationTests extends AbstractKeyspaceCrea assertThat(column.getType(), is(equalTo((DataType) DataType.list(DataType.ascii())))); } + /** + * @see DATACASS-192 + */ + @Test + public void alterTableDropColumn() { + + session.execute("CREATE TABLE addamsFamily (name varchar PRIMARY KEY, gender varchar);"); + + AlterTableSpecification spec = AlterTableSpecification.alterTable("addamsFamily").drop("gender"); + + execute(spec); + + assertThat(getTableMetadata("addamsfamily").getColumn("gender"), is(nullValue())); + } + + /** + * @see DATACASS-192 + */ + @Test + public void alterTableRenameColumn() { + + session.execute("CREATE TABLE addamsFamily (name varchar PRIMARY KEY, firstname varchar);"); + + AlterTableSpecification spec = AlterTableSpecification.alterTable("addamsFamily").rename("name", "newname"); + + execute(spec); + + assertThat(getTableMetadata("addamsfamily").getColumn("name"), is(nullValue())); + assertThat(getTableMetadata("addamsfamily").getColumn("newname"), is(notNullValue())); + } + + /** + * @see DATACASS-192 + */ + @Test + public void alterTableAddCaching() { + + session.execute("CREATE TABLE users (user_name varchar PRIMARY KEY);"); + + Map cachingMap = new LinkedHashMap(); + cachingMap.put(CachingOption.KEYS, KeyCachingOption.NONE); + cachingMap.put(CachingOption.ROWS_PER_PARTITION, "15"); + + AlterTableSpecification spec = AlterTableSpecification.alterTable("users").with(TableOption.CACHING, cachingMap); + + execute(spec); + + assertThat(getTableMetadata("users").getOptions().getCaching().get("keys"), is(equalTo("NONE"))); + assertThat(getTableMetadata("users").getOptions().getCaching().get("rows_per_partition"), is(equalTo("15"))); + + } + private void execute(AlterTableSpecification spec) { session.execute(new AlterTableCqlGenerator(spec).toCql()); } diff --git a/spring-cql/src/test/java/org/springframework/cassandra/core/cql/generator/AlterTableCqlGeneratorUnitTests.java b/spring-cql/src/test/java/org/springframework/cassandra/core/cql/generator/AlterTableCqlGeneratorUnitTests.java index bc63c710f..98a41e36a 100644 --- a/spring-cql/src/test/java/org/springframework/cassandra/core/cql/generator/AlterTableCqlGeneratorUnitTests.java +++ b/spring-cql/src/test/java/org/springframework/cassandra/core/cql/generator/AlterTableCqlGeneratorUnitTests.java @@ -18,8 +18,14 @@ package org.springframework.cassandra.core.cql.generator; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import java.util.LinkedHashMap; +import java.util.Map; + import org.junit.Test; import org.springframework.cassandra.core.keyspace.AlterTableSpecification; +import org.springframework.cassandra.core.keyspace.TableOption; +import org.springframework.cassandra.core.keyspace.TableOption.CachingOption; +import org.springframework.cassandra.core.keyspace.TableOption.KeyCachingOption; import com.datastax.driver.core.DataType; @@ -80,6 +86,71 @@ public class AlterTableCqlGeneratorUnitTests { assertThat(toCql(spec), is(equalTo("ALTER TABLE users ADD top_places list;"))); } + /** + * @see DATACASS-192 + */ + @Test + public void alterTableDropColumn() { + + AlterTableSpecification spec = AlterTableSpecification.alterTable("addamsFamily").drop("gender"); + + assertThat(toCql(spec), is(equalTo("ALTER TABLE addamsfamily DROP gender;"))); + } + + /** + * @see DATACASS-192 + */ + @Test + public void alterTableRenameColumn() { + + AlterTableSpecification spec = AlterTableSpecification.alterTable("addamsFamily").rename("firstname", "lastname"); + + assertThat(toCql(spec), is(equalTo("ALTER TABLE addamsfamily RENAME firstname TO lastname;"))); + } + + /** + * @see DATACASS-192 + */ + @Test + public void alterTableAddCommentAndTableOption() { + + AlterTableSpecification spec = AlterTableSpecification.alterTable("addamsFamily") + .with(TableOption.READ_REPAIR_CHANCE, 0.2f).with(TableOption.COMMENT, "A most excellent and useful table"); + + assertThat(toCql(spec), is(equalTo( + "ALTER TABLE addamsfamily WITH read_repair_chance = 0.2 AND comment = 'A most excellent and useful table';"))); + } + + /** + * @see DATACASS-192 + */ + @Test + public void alterTableAddColumnAndComment() { + + AlterTableSpecification spec = AlterTableSpecification.alterTable("addamsFamily") + .add("top_places", DataType.list(DataType.ascii())).add("other", DataType.list(DataType.ascii())) + .with(TableOption.COMMENT, "A most excellent and useful table"); + + assertThat(toCql(spec), is(equalTo( + "ALTER TABLE addamsfamily ADD top_places list ADD other list WITH comment = 'A most excellent and useful table';"))); + } + + /** + * @see DATACASS-192 + */ + @Test + public void alterTableAddCaching() { + + Map cachingMap = new LinkedHashMap(); + cachingMap.put(CachingOption.KEYS, KeyCachingOption.NONE); + cachingMap.put(CachingOption.ROWS_PER_PARTITION, "15"); + + AlterTableSpecification spec = AlterTableSpecification.alterTable("users").with(TableOption.CACHING, cachingMap); + + assertThat(toCql(spec), + is(equalTo("ALTER TABLE users WITH caching = { 'keys' : 'none', 'rows_per_partition' : '15' };"))); + } + private String toCql(AlterTableSpecification spec) { return new AlterTableCqlGenerator(spec).toCql(); }