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.
This commit is contained in:
Mark Paluch
2016-06-21 15:52:10 +02:00
committed by John Blum
parent 38fef212e4
commit f1e3ef48e8
8 changed files with 329 additions and 25 deletions

View File

@@ -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 <code>ADD</code> clause of an <code>ALTER TABLE</code> 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<AddColumnSpecification> {
@@ -31,8 +33,12 @@ public class AddColumnCqlGenerator extends ColumnChangeCqlGenerator<AddColumnSpe
super(specification);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.cql.generator.ColumnChangeCqlGenerator#toCql(java.lang.StringBuilder)
*/
@Override
public StringBuilder toCql(StringBuilder cql) {
return noNull(cql).append("ADD ").append(spec().getName()).append(' ').append(spec().getType().asFunctionParameterString());
return noNull(cql).append("ADD ").append(spec().getName()).append(' ')
.append(spec().getType().asFunctionParameterString());
}
}

View File

@@ -15,7 +15,7 @@
*/
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 java.util.Map;
@@ -25,19 +25,33 @@ import org.springframework.cassandra.core.keyspace.AlterTableSpecification;
import org.springframework.cassandra.core.keyspace.ColumnChangeSpecification;
import org.springframework.cassandra.core.keyspace.DropColumnSpecification;
import org.springframework.cassandra.core.keyspace.Option;
import org.springframework.cassandra.core.keyspace.RenameColumnSpecification;
import org.springframework.cassandra.core.keyspace.TableOption;
/**
* CQL generator for generating <code>ALTER TABLE</code> statements.
* CQL generator for generating {@code ALTER TABLE} statements.
*
* @author Matthew T. Adams
* @author Mark Paluch
* @see AlterTableSpecification
*/
public class AlterTableCqlGenerator extends TableOptionsCqlGenerator<AlterTableSpecification> {
/**
* 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<AlterTableS
cql = noNull(cql);
preambleCql(cql);
changesCql(cql);
optionsCql(cql);
if (!spec().getChanges().isEmpty()) {
cql.append(' ');
changesCql(cql);
}
if (!spec().getOptions().isEmpty()) {
cql.append(' ');
optionsCql(cql);
}
cql.append(";");
@@ -56,7 +78,7 @@ public class AlterTableCqlGenerator extends TableOptionsCqlGenerator<AlterTableS
}
protected StringBuilder preambleCql(StringBuilder cql) {
return noNull(cql).append("ALTER TABLE ").append(spec().getName()).append(" ");
return noNull(cql).append("ALTER TABLE ").append(spec().getName());
}
protected StringBuilder changesCql(StringBuilder cql) {
@@ -76,20 +98,29 @@ public class AlterTableCqlGenerator extends TableOptionsCqlGenerator<AlterTableS
}
protected ColumnChangeCqlGenerator<?> 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<String, Object> options = spec().getOptions();
@@ -97,7 +128,7 @@ public class AlterTableCqlGenerator extends TableOptionsCqlGenerator<AlterTableS
return cql;
}
cql.append(" WITH ");
cql.append("WITH ");
boolean first = true;
for (String key : options.keySet()) {

View File

@@ -20,9 +20,11 @@ import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import org.springframework.cassandra.core.keyspace.DropColumnSpecification;
/**
* CQL generator for generating a <code>DROP</code> column clause of an <code>ALTER TABLE</code> 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<DropColumnSpecification> {

View File

@@ -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<RenameColumnSpecification> {
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());
}
}

View File

@@ -24,9 +24,17 @@ import org.springframework.cassandra.core.cql.CqlIdentifier;
import com.datastax.driver.core.DataType;
/**
* Builder class to construct an <code>ALTER TABLE</code> 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<AlterTableSpecification> {
@@ -60,17 +68,22 @@ public class AlterTableSpecification extends TableOptionsSpecification<AlterTabl
private List<ColumnChangeSpecification> changes = new ArrayList<ColumnChangeSpecification>();
/*
* Adds a <code>DROP</code> 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 <code>ADD</code> 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 TableOptionsSpecification<AlterTabl
}
/**
* Adds an <code>ALTER</code> 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<AlterTabl
public List<ColumnChangeSpecification> getChanges() {
return Collections.unmodifiableList(changes);
}
}

View File

@@ -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;
}
}

View File

@@ -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<ascii>);");
session.execute(
"CREATE TABLE addamsFamily (name varchar PRIMARY KEY, gender varchar,\n" + " lastknownlocation list<ascii>);");
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<Object, Object> cachingMap = new LinkedHashMap<Object, Object>();
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());
}

View File

@@ -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<ascii>;")));
}
/**
* @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<ascii> ADD other list<ascii> WITH comment = 'A most excellent and useful table';")));
}
/**
* @see DATACASS-192
*/
@Test
public void alterTableAddCaching() {
Map<Object, Object> cachingMap = new LinkedHashMap<Object, Object>();
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();
}