Repository: cassandra Updated Branches: refs/heads/trunk 1c0c39de7 -> ca2e71c38
Add/drop multiple columns in one ALTER TABLE statement patch by Amit Singh Chowdhery; reviewed by Robert Stupp for CASSANDRA-10411 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/ca2e71c3 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/ca2e71c3 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/ca2e71c3 Branch: refs/heads/trunk Commit: ca2e71c3814025dea776f84ab72c1d563eb888d5 Parents: 1c0c39d Author: Amit Singh Chowdhery <amit.f.si...@ericsson.com> Authored: Mon Mar 28 19:06:42 2016 +0200 Committer: Robert Stupp <sn...@snazy.de> Committed: Mon Mar 28 19:06:42 2016 +0200 ---------------------------------------------------------------------- CHANGES.txt | 1 + doc/cql3/CQL.textile | 3 + src/java/org/apache/cassandra/cql3/Cql.g | 26 +- .../cql3/statements/AlterTableStatement.java | 279 ++++++++++--------- .../statements/AlterTableStatementColumn.java | 53 ++++ .../cql3/validation/operations/AlterTest.java | 81 ++++++ 6 files changed, 308 insertions(+), 135 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca2e71c3/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index b80fdf3..8e9cc44 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.6 + * Add/drop multiple columns in one ALTER TABLE statement (CASSANDRA-10411) * Add require_endpoint_verification opt for internode encryption (CASSANDRA-9220) * Add auto import java.util for UDF code block (CASSANDRA-11392) * Add --hex-format option to nodetool getsstables (CASSANDRA-11337) http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca2e71c3/doc/cql3/CQL.textile ---------------------------------------------------------------------- diff --git a/doc/cql3/CQL.textile b/doc/cql3/CQL.textile index 336a64c..1ee2537 100644 --- a/doc/cql3/CQL.textile +++ b/doc/cql3/CQL.textile @@ -396,7 +396,9 @@ bc(syntax).. <instruction> ::= ALTER <identifier> TYPE <type> | ADD <identifier> <type> + | ADD ( <identifier> <type> ( , <identifier> <type> )* ) | DROP <identifier> + | DROP ( <identifier> ( , <identifier> )* ) | WITH <option> ( AND <option> )* p. __Sample:__ @@ -2312,6 +2314,7 @@ The following describes the changes in each version of CQL. h3. 3.4.2 * "@INSERT/UPDATE options@":#updateOptions for tables having a default_time_to_live specifying a TTL of 0 will remove the TTL from the inserted or updated values +* "@ALTER TABLE@":#alterTableStmt @ADD@ and @DROP@ now allow mutiple columns to be added/removed h3. 3.4.1 http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca2e71c3/src/java/org/apache/cassandra/cql3/Cql.g ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g index 5cb479c..f7841fd 100644 --- a/src/java/org/apache/cassandra/cql3/Cql.g +++ b/src/java/org/apache/cassandra/cql3/Cql.g @@ -848,8 +848,8 @@ alterKeyspaceStatement returns [AlterKeyspaceStatement expr] /** * ALTER COLUMN FAMILY <CF> ALTER <column> TYPE <newtype>; - * ALTER COLUMN FAMILY <CF> ADD <column> <newtype>; - * ALTER COLUMN FAMILY <CF> DROP <column>; + * ALTER COLUMN FAMILY <CF> ADD <column> <newtype>; | ALTER COLUMN FAMILY <CF> ADD (<column> <newtype>,<column1> <newtype1>..... <column n> <newtype n>) + * ALTER COLUMN FAMILY <CF> DROP <column>; | ALTER COLUMN FAMILY <CF> DROP ( <column>,<column1>.....<column n>) * ALTER COLUMN FAMILY <CF> WITH <property> = <value>; * ALTER COLUMN FAMILY <CF> RENAME <column> TO <column>; */ @@ -858,19 +858,31 @@ alterTableStatement returns [AlterTableStatement expr] AlterTableStatement.Type type = null; TableAttributes attrs = new TableAttributes(); Map<ColumnIdentifier.Raw, ColumnIdentifier.Raw> renames = new HashMap<ColumnIdentifier.Raw, ColumnIdentifier.Raw>(); - boolean isStatic = false; + List<AlterTableStatementColumn> colNameList = new ArrayList<AlterTableStatementColumn>(); } : K_ALTER K_COLUMNFAMILY cf=columnFamilyName - ( K_ALTER id=cident K_TYPE v=comparatorType { type = AlterTableStatement.Type.ALTER; } - | K_ADD id=cident v=comparatorType ({ isStatic=true; } K_STATIC)? { type = AlterTableStatement.Type.ADD; } - | K_DROP id=cident { type = AlterTableStatement.Type.DROP; } + ( K_ALTER id=cident K_TYPE v=comparatorType { type = AlterTableStatement.Type.ALTER; } { colNameList.add(new AlterTableStatementColumn(id,v)); } + | K_ADD ( (id=cident v=comparatorType b1=cfisStatic { colNameList.add(new AlterTableStatementColumn(id,v,b1)); }) + | ('(' id1=cident v1=comparatorType b1=cfisStatic { colNameList.add(new AlterTableStatementColumn(id1,v1,b1)); } + ( ',' idn=cident vn=comparatorType bn=cfisStatic { colNameList.add(new AlterTableStatementColumn(idn,vn,bn)); } )* ')' ) ) { type = AlterTableStatement.Type.ADD; } + | K_DROP ( id=cident { colNameList.add(new AlterTableStatementColumn(id)); } + | ('(' id1=cident { colNameList.add(new AlterTableStatementColumn(id1)); } + ( ',' idn=cident { colNameList.add(new AlterTableStatementColumn(idn)); } )* ')') ) { type = AlterTableStatement.Type.DROP; } | K_WITH properties[attrs] { type = AlterTableStatement.Type.OPTS; } | K_RENAME { type = AlterTableStatement.Type.RENAME; } id1=cident K_TO toId1=cident { renames.put(id1, toId1); } ( K_AND idn=cident K_TO toIdn=cident { renames.put(idn, toIdn); } )* ) { - $expr = new AlterTableStatement(cf, type, id, v, attrs, renames, isStatic); + $expr = new AlterTableStatement(cf, type, colNameList, attrs, renames); + } + ; + +cfisStatic returns [boolean isStaticColumn] + @init{ + boolean isStatic = false; + } + : (K_STATIC { isStatic=true; })? { $isStaticColumn = isStatic; } ; http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca2e71c3/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java index 3515c6b..8ad3015 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java @@ -52,27 +52,21 @@ public class AlterTableStatement extends SchemaAlteringStatement } public final Type oType; - public final CQL3Type.Raw validator; - public final ColumnIdentifier.Raw rawColumnName; private final TableAttributes attrs; private final Map<ColumnIdentifier.Raw, ColumnIdentifier.Raw> renames; - private final boolean isStatic; // Only for ALTER ADD + private final List<AlterTableStatementColumn> colNameList; public AlterTableStatement(CFName name, Type type, - ColumnIdentifier.Raw columnName, - CQL3Type.Raw validator, + List<AlterTableStatementColumn> colDataList, TableAttributes attrs, - Map<ColumnIdentifier.Raw, ColumnIdentifier.Raw> renames, - boolean isStatic) + Map<ColumnIdentifier.Raw, ColumnIdentifier.Raw> renames) { super(name); this.oType = type; - this.rawColumnName = columnName; - this.validator = validator; // used only for ADD/ALTER commands + this.colNameList = colDataList; this.attrs = attrs; this.renames = renames; - this.isStatic = isStatic; } public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException @@ -92,15 +86,12 @@ public class AlterTableStatement extends SchemaAlteringStatement throw new InvalidRequestException("Cannot use ALTER TABLE on Materialized View"); CFMetaData cfm = meta.copy(); - - CQL3Type validator = this.validator == null ? null : this.validator.prepare(keyspace()); ColumnIdentifier columnName = null; ColumnDefinition def = null; - if (rawColumnName != null) - { - columnName = rawColumnName.prepare(cfm); - def = cfm.getColumnDefinition(columnName); - } + CQL3Type.Raw dataType = null; + boolean isStatic = false; + CQL3Type validator = null; + ColumnIdentifier.Raw rawColumnName = null; List<ViewDefinition> viewUpdates = null; Iterable<ViewDefinition> views = View.findAll(keyspace(), columnFamily()); @@ -108,81 +99,104 @@ public class AlterTableStatement extends SchemaAlteringStatement switch (oType) { case ADD: - assert columnName != null; - if (cfm.isDense()) - throw new InvalidRequestException("Cannot add new column to a COMPACT STORAGE table"); - - if (isStatic) + for (AlterTableStatementColumn colData : colNameList) { - if (!cfm.isCompound()) - throw new InvalidRequestException("Static columns are not allowed in COMPACT STORAGE tables"); - if (cfm.clusteringColumns().isEmpty()) - throw new InvalidRequestException("Static columns are only useful (and thus allowed) if the table has at least one clustering column"); - } + columnName = null; + rawColumnName = colData.getColumnName(); + if (rawColumnName != null) + { + columnName = rawColumnName.prepare(cfm); + def = cfm.getColumnDefinition(columnName); + dataType = colData.getColumnType(); + isStatic = colData.getStaticType(); + validator = dataType == null ? null : dataType.prepare(keyspace()); + } - if (def != null) - { - switch (def.kind) + assert columnName != null; + if (cfm.isDense()) + throw new InvalidRequestException("Cannot add new column to a COMPACT STORAGE table"); + + if (isStatic) { - case PARTITION_KEY: - case CLUSTERING: - throw new InvalidRequestException(String.format("Invalid column name %s because it conflicts with a PRIMARY KEY part", columnName)); - default: - throw new InvalidRequestException(String.format("Invalid column name %s because it conflicts with an existing column", columnName)); + if (!cfm.isCompound()) + throw new InvalidRequestException("Static columns are not allowed in COMPACT STORAGE tables"); + if (cfm.clusteringColumns().isEmpty()) + throw new InvalidRequestException("Static columns are only useful (and thus allowed) if the table has at least one clustering column"); } - } - // Cannot re-add a dropped counter column. See #7831. - if (meta.isCounter() && meta.getDroppedColumns().containsKey(columnName.bytes)) - throw new InvalidRequestException(String.format("Cannot re-add previously dropped counter column %s", columnName)); + if (def != null) + { + switch (def.kind) + { + case PARTITION_KEY: + case CLUSTERING: + throw new InvalidRequestException(String.format("Invalid column name %s because it conflicts with a PRIMARY KEY part", columnName)); + default: + throw new InvalidRequestException(String.format("Invalid column name %s because it conflicts with an existing column", columnName)); + } + } - AbstractType<?> type = validator.getType(); - if (type.isCollection() && type.isMultiCell()) - { - if (!cfm.isCompound()) - throw new InvalidRequestException("Cannot use non-frozen collections in COMPACT STORAGE tables"); - if (cfm.isSuper()) - throw new InvalidRequestException("Cannot use non-frozen collections with super column families"); - - // If there used to be a non-frozen collection column with the same name (that has been dropped), - // we could still have some data using the old type, and so we can't allow adding a collection - // with the same name unless the types are compatible (see #6276). - CFMetaData.DroppedColumn dropped = cfm.getDroppedColumns().get(columnName.bytes); - if (dropped != null && dropped.type instanceof CollectionType - && dropped.type.isMultiCell() && !type.isCompatibleWith(dropped.type)) + // Cannot re-add a dropped counter column. See #7831. + if (meta.isCounter() && meta.getDroppedColumns().containsKey(columnName.bytes)) + throw new InvalidRequestException(String.format("Cannot re-add previously dropped counter column %s", columnName)); + + AbstractType<?> type = validator.getType(); + if (type.isCollection() && type.isMultiCell()) { - String message = - String.format("Cannot add a collection with the name %s because a collection with the same name" - + " and a different type (%s) has already been used in the past", - columnName, - dropped.type.asCQL3Type()); - throw new InvalidRequestException(message); + if (!cfm.isCompound()) + throw new InvalidRequestException("Cannot use non-frozen collections in COMPACT STORAGE tables"); + if (cfm.isSuper()) + throw new InvalidRequestException("Cannot use non-frozen collections with super column families"); + + // If there used to be a non-frozen collection column with the same name (that has been dropped), + // we could still have some data using the old type, and so we can't allow adding a collection + // with the same name unless the types are compatible (see #6276). + CFMetaData.DroppedColumn dropped = cfm.getDroppedColumns().get(columnName.bytes); + if (dropped != null && dropped.type instanceof CollectionType + && dropped.type.isMultiCell() && !type.isCompatibleWith(dropped.type)) + { + String message = + String.format("Cannot add a collection with the name %s because a collection with the same name" + + " and a different type (%s) has already been used in the past", + columnName, + dropped.type.asCQL3Type()); + throw new InvalidRequestException(message); + } } - } - cfm.addColumnDefinition(isStatic - ? ColumnDefinition.staticDef(cfm, columnName.bytes, type) - : ColumnDefinition.regularDef(cfm, columnName.bytes, type)); + cfm.addColumnDefinition(isStatic + ? ColumnDefinition.staticDef(cfm, columnName.bytes, type) + : ColumnDefinition.regularDef(cfm, columnName.bytes, type)); - // Adding a column to a table which has an include all view requires the column to be added to the view - // as well - if (!isStatic) - { - for (ViewDefinition view : views) + // Adding a column to a table which has an include all view requires the column to be added to the view + // as well + if (!isStatic) { - if (view.includeAllColumns) + for (ViewDefinition view : views) { - ViewDefinition viewCopy = view.copy(); - viewCopy.metadata.addColumnDefinition(ColumnDefinition.regularDef(viewCopy.metadata, columnName.bytes, type)); - if (viewUpdates == null) - viewUpdates = new ArrayList<>(); - viewUpdates.add(viewCopy); + if (view.includeAllColumns) + { + ViewDefinition viewCopy = view.copy(); + viewCopy.metadata.addColumnDefinition(ColumnDefinition.regularDef(viewCopy.metadata, columnName.bytes, type)); + if (viewUpdates == null) + viewUpdates = new ArrayList<>(); + viewUpdates.add(viewCopy); + } } } } break; case ALTER: + rawColumnName = colNameList.get(0).getColumnName(); + if (rawColumnName != null) + { + columnName = rawColumnName.prepare(cfm); + def = cfm.getColumnDefinition(columnName); + dataType = colNameList.get(0).getColumnType(); + validator = dataType == null ? null : dataType.prepare(keyspace()); + } + assert columnName != null; if (def == null) throw new InvalidRequestException(String.format("Column %s was not found in table %s", columnName, columnFamily())); @@ -214,66 +228,76 @@ public class AlterTableStatement extends SchemaAlteringStatement break; case DROP: - assert columnName != null; - if (!cfm.isCQLTable()) - throw new InvalidRequestException("Cannot drop columns from a non-CQL3 table"); - if (def == null) - throw new InvalidRequestException(String.format("Column %s was not found in table %s", columnName, columnFamily())); - - switch (def.kind) + for (AlterTableStatementColumn colData : colNameList) { - case PARTITION_KEY: - case CLUSTERING: - throw new InvalidRequestException(String.format("Cannot drop PRIMARY KEY part %s", columnName)); - case REGULAR: - case STATIC: - ColumnDefinition toDelete = null; - for (ColumnDefinition columnDef : cfm.partitionColumns()) - { - if (columnDef.name.equals(columnName)) - { - toDelete = columnDef; - break; - } - } + columnName = null; + rawColumnName = colData.getColumnName(); + if (rawColumnName != null) + { + columnName = rawColumnName.prepare(cfm); + def = cfm.getColumnDefinition(columnName); + } + assert columnName != null; + if (!cfm.isCQLTable()) + throw new InvalidRequestException("Cannot drop columns from a non-CQL3 table"); + if (def == null) + throw new InvalidRequestException(String.format("Column %s was not found in table %s", columnName, columnFamily())); + + switch (def.kind) + { + case PARTITION_KEY: + case CLUSTERING: + throw new InvalidRequestException(String.format("Cannot drop PRIMARY KEY part %s", columnName)); + case REGULAR: + case STATIC: + ColumnDefinition toDelete = null; + for (ColumnDefinition columnDef : cfm.partitionColumns()) + { + if (columnDef.name.equals(columnName)) + { + toDelete = columnDef; + break; + } + } assert toDelete != null; cfm.removeColumnDefinition(toDelete); cfm.recordColumnDrop(toDelete); break; - } + } - // If the dropped column is required by any secondary indexes - // we reject the operation, as the indexes must be dropped first - Indexes allIndexes = cfm.getIndexes(); - if (!allIndexes.isEmpty()) - { - ColumnFamilyStore store = Keyspace.openAndGetStore(cfm); - Set<IndexMetadata> dependentIndexes = store.indexManager.getDependentIndexes(def); - if (!dependentIndexes.isEmpty()) - throw new InvalidRequestException(String.format("Cannot drop column %s because it has " + - "dependent secondary indexes (%s)", - def, - dependentIndexes.stream() - .map(i -> i.name) - .collect(Collectors.joining(",")))); - } + // If the dropped column is required by any secondary indexes + // we reject the operation, as the indexes must be dropped first + Indexes allIndexes = cfm.getIndexes(); + if (!allIndexes.isEmpty()) + { + ColumnFamilyStore store = Keyspace.openAndGetStore(cfm); + Set<IndexMetadata> dependentIndexes = store.indexManager.getDependentIndexes(def); + if (!dependentIndexes.isEmpty()) + throw new InvalidRequestException(String.format("Cannot drop column %s because it has " + + "dependent secondary indexes (%s)", + def, + dependentIndexes.stream() + .map(i -> i.name) + .collect(Collectors.joining(",")))); + } - // If a column is dropped which is included in a view, we don't allow the drop to take place. - boolean rejectAlter = false; - StringBuilder builder = new StringBuilder(); - for (ViewDefinition view : views) - { - if (!view.includes(columnName)) continue; + // If a column is dropped which is included in a view, we don't allow the drop to take place. + boolean rejectAlter = false; + StringBuilder builder = new StringBuilder(); + for (ViewDefinition view : views) + { + if (!view.includes(columnName)) continue; + if (rejectAlter) + builder.append(','); + rejectAlter = true; + builder.append(view.viewName); + } if (rejectAlter) - builder.append(','); - rejectAlter = true; - builder.append(view.viewName); + throw new InvalidRequestException(String.format("Cannot drop column %s, depended on by materialized views (%s.{%s})", + columnName.toString(), + keyspace(), + builder.toString())); } - if (rejectAlter) - throw new InvalidRequestException(String.format("Cannot drop column %s, depended on by materialized views (%s.{%s})", - columnName.toString(), - keyspace(), - builder.toString())); break; case OPTS: if (attrs == null) @@ -379,12 +403,11 @@ public class AlterTableStatement extends SchemaAlteringStatement } } + @Override public String toString() { return String.format("AlterTableStatement(name=%s, type=%s, column=%s, validator=%s)", cfName, - oType, - rawColumnName, - validator); + oType); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca2e71c3/src/java/org/apache/cassandra/cql3/statements/AlterTableStatementColumn.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatementColumn.java b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatementColumn.java new file mode 100644 index 0000000..a5d7de7 --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatementColumn.java @@ -0,0 +1,53 @@ +/* + * 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; + +import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.ColumnIdentifier; + +public class AlterTableStatementColumn { + private final CQL3Type.Raw dataType; + private final ColumnIdentifier.Raw colName; + private final Boolean isStatic; + + public AlterTableStatementColumn(ColumnIdentifier.Raw colName, CQL3Type.Raw dataType, boolean isStatic) { + this.dataType = dataType; + this.colName = colName; + this.isStatic = isStatic; + } + + public AlterTableStatementColumn(ColumnIdentifier.Raw colName, CQL3Type.Raw dataType) { + this(colName, dataType,false ); + } + + public AlterTableStatementColumn(ColumnIdentifier.Raw colName) { + this(colName, null, false); + } + + public CQL3Type.Raw getColumnType() { + return dataType; + } + + public ColumnIdentifier.Raw getColumnName() { + return colName; + } + + public Boolean getStaticType() { + return isStatic; + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca2e71c3/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java index 9f8bea2..3eb55fd 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java @@ -324,4 +324,85 @@ public class AlterTest extends CQLTester createTable("CREATE TABLE %s (key blob, column1 blob, value blob, PRIMARY KEY ((key), column1)) WITH COMPACT STORAGE"); assertInvalidThrow(InvalidRequestException.class, "ALTER TABLE %s ALTER column1 TYPE ascii"); } + + /* + * Test case to check addition of one column + */ + @Test + public void testAlterAddOneColumn() throws Throwable + { + createTable("CREATE TABLE IF NOT EXISTS %s (id int, name text, PRIMARY KEY (id))"); + alterTable("ALTER TABLE %s add mail text;"); + + assertColumnNames(execute("SELECT * FROM %s"), "id", "mail", "name"); + } + + /* + * Test case to check addition of more than one column + */ + @Test + public void testAlterAddMultiColumn() throws Throwable + { + createTable("CREATE TABLE IF NOT EXISTS %s (id int, yearofbirth int, PRIMARY KEY (id))"); + alterTable("ALTER TABLE %s add (firstname text, password blob, lastname text, \"SOME escaped col\" bigint)"); + + assertColumnNames(execute("SELECT * FROM %s"), "id", "SOME escaped col", "firstname", "lastname", "password", "yearofbirth"); + } + + /* + * Should throw SyntaxException if multiple columns are added using wrong syntax. + * Expected Syntax : Alter table T1 add (C1 datatype,C2 datatype,C3 datatype) + */ + @Test(expected = SyntaxException.class) + public void testAlterAddMultiColumnWithoutBraces() throws Throwable + { + execute("ALTER TABLE %s.users add lastname text, password blob, yearofbirth int;"); + } + + /* + * Test case to check deletion of one column + */ + @Test + public void testAlterDropOneColumn() throws Throwable + { + createTable("CREATE TABLE IF NOT EXISTS %s (id text, telephone int, yearofbirth int, PRIMARY KEY (id))"); + alterTable("ALTER TABLE %s drop telephone"); + + assertColumnNames(execute("SELECT * FROM %s"), "id", "yearofbirth"); + } + + @Test + /* + * Test case to check deletion of more than one column + */ + public void testAlterDropMultiColumn() throws Throwable + { + createTable("CREATE TABLE IF NOT EXISTS %s (id text, address text, telephone int, yearofbirth int, \"SOME escaped col\" bigint, PRIMARY KEY (id))"); + alterTable("ALTER TABLE %s drop (address, telephone, \"SOME escaped col\");"); + + assertColumnNames(execute("SELECT * FROM %s"), "id", "yearofbirth"); + } + + /* + * Should throw SyntaxException if multiple columns are dropped using wrong syntax. + */ + @Test(expected = SyntaxException.class) + public void testAlterDeletionColumnWithoutBraces() throws Throwable + { + execute("ALTER TABLE %s.users drop name,address;"); + } + + @Test(expected = InvalidRequestException.class) + public void testAlterAddDuplicateColumn() throws Throwable + { + createTable("CREATE TABLE IF NOT EXISTS %s (id text, address text, telephone int, yearofbirth int, PRIMARY KEY (id))"); + execute("ALTER TABLE %s add (salary int, salary int);"); + } + + @Test(expected = InvalidRequestException.class) + public void testAlterDropDuplicateColumn() throws Throwable + { + createTable("CREATE TABLE IF NOT EXISTS %s (id text, address text, telephone int, yearofbirth int, PRIMARY KEY (id))"); + execute("ALTER TABLE %s drop (address, address);"); + } }