You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by jb...@apache.org on 2011/06/01 17:58:45 UTC
svn commit: r1130200 - in /cassandra/branches/cassandra-0.8: ./ doc/cql/
src/java/org/apache/cassandra/cql/ test/system/
Author: jbellis
Date: Wed Jun 1 15:58:44 2011
New Revision: 1130200
URL: http://svn.apache.org/viewvc?rev=1130200&view=rev
Log:
add CQL ALTER TABLE
patch by pyaskevich; reviewed by jbellis for CASSANDRA-1709
Added:
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/AlterTableStatement.java
Modified:
cassandra/branches/cassandra-0.8/CHANGES.txt
cassandra/branches/cassandra-0.8/doc/cql/CQL.textile
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/Cql.g
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/StatementType.java
cassandra/branches/cassandra-0.8/test/system/test_cql.py
Modified: cassandra/branches/cassandra-0.8/CHANGES.txt
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/CHANGES.txt?rev=1130200&r1=1130199&r2=1130200&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/CHANGES.txt (original)
+++ cassandra/branches/cassandra-0.8/CHANGES.txt Wed Jun 1 15:58:44 2011
@@ -32,6 +32,7 @@
* Fixed rows being cached if they do not exist (CASSANDRA-2723)
* fix truncate/compaction race (CASSANDRA-2673)
* improve CQL JDBC spec compliance (CASSANDRA-2720)
+ * add CQL ALTER TABLE (CASSANDRA-1709)
0.8.0-final
Modified: cassandra/branches/cassandra-0.8/doc/cql/CQL.textile
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/doc/cql/CQL.textile?rev=1130200&r1=1130199&r2=1130200&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/doc/cql/CQL.textile (original)
+++ cassandra/branches/cassandra-0.8/doc/cql/CQL.textile Wed Jun 1 15:58:44 2011
@@ -69,6 +69,17 @@ SELECT ... WHERE <CLAUSE> [LIMIT N] ...
Limiting the number of rows returned can be achieved by adding the @LIMIT@ option to a @SELECT@ expression. @LIMIT@ defaults to 10,000 when left unset.
+h2. ALTER TABLE
+
+_Synopsis:_
+
+bc.
+ALTER TABLE <columnFamily> ADD <column> <validator>;
+ALTER TABLE <columnFamily> ALTER <column> TYPE <validator>;
+ALTER TABLE <columnFamily> DROP <column>;
+
+An @ALTER@ is used to manipulate with ColumnFamily columns. It allows you to add new columns, alter and drop existing columns. No results are returned.
+
h2. INSERT
_Synopsis:_
Added: cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/AlterTableStatement.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/AlterTableStatement.java?rev=1130200&view=auto
==============================================================================
--- cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/AlterTableStatement.java (added)
+++ cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/AlterTableStatement.java Wed Jun 1 15:58:44 2011
@@ -0,0 +1,122 @@
+/*
+ *
+ * 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.cql;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.config.ConfigurationException;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.db.marshal.TypeParser;
+import org.apache.cassandra.db.migration.avro.CfDef;
+import org.apache.cassandra.db.migration.avro.ColumnDef;
+import org.apache.cassandra.thrift.InvalidRequestException;
+
+import java.nio.ByteBuffer;
+
+public class AlterTableStatement
+{
+ public static enum OperationType
+ {
+ ADD, ALTER, DROP
+ }
+
+ public final OperationType oType;
+ public final String columnFamily, columnName, validator;
+
+ public AlterTableStatement(String columnFamily, OperationType type, String columnName)
+ {
+ this(columnFamily, type, columnName, null);
+ }
+
+ public AlterTableStatement(String columnFamily, OperationType type, String columnName, String validator)
+ {
+ this.columnFamily = columnFamily;
+ this.oType = type;
+ this.columnName = columnName;
+ this.validator = CreateColumnFamilyStatement.comparators.get(validator); // used only for ADD/ALTER commands
+ }
+
+ public CfDef getCfDef(String keyspace) throws ConfigurationException, InvalidRequestException
+ {
+ CFMetaData meta = DatabaseDescriptor.getCFMetaData(keyspace, columnFamily);
+
+ CfDef cfDef = CFMetaData.convertToAvro(meta);
+
+ ByteBuffer columnName = meta.comparator.fromString(this.columnName);
+
+ switch (oType)
+ {
+ case ADD:
+ cfDef.column_metadata.add(new ColumnDefinition(columnName,
+ TypeParser.parse(validator),
+ null,
+ null).deflate());
+ break;
+
+ case ALTER:
+ ColumnDefinition column = meta.getColumnDefinition(columnName);
+
+ if (column == null)
+ throw new InvalidRequestException(String.format("Column '%s' was not found in CF '%s'",
+ this.columnName,
+ columnFamily));
+
+ column.setValidator(TypeParser.parse(validator));
+
+ cfDef.column_metadata.add(column.deflate());
+ break;
+
+ case DROP:
+ ColumnDef toDelete = null;
+
+ for (ColumnDef columnDef : cfDef.column_metadata)
+ {
+ if (columnDef.name.equals(columnName))
+ {
+ toDelete = columnDef;
+ }
+ }
+
+ if (toDelete == null)
+ throw new InvalidRequestException(String.format("Column '%s' was not found in CF '%s'",
+ this.columnName,
+ columnFamily));
+
+ // it is impossible to use ColumnDefinition.deflate() in remove() method
+ // it will throw java.lang.ClassCastException: java.lang.String cannot be cast to org.apache.avro.util.Utf8
+ // some where deep inside of Avro
+ cfDef.column_metadata.remove(toDelete);
+ break;
+ }
+
+ return cfDef;
+ }
+
+ public String toString()
+ {
+ return String.format("AlterTableStatement(cf=%s, type=%s, column=%s, validator=%s)",
+ columnFamily,
+ oType,
+ columnName,
+ validator);
+ }
+
+}
Modified: cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/Cql.g
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/Cql.g?rev=1130200&r1=1130199&r2=1130200&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/Cql.g (original)
+++ cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/Cql.g Wed Jun 1 15:58:44 2011
@@ -35,6 +35,8 @@ options {
import java.util.ArrayList;
import org.apache.cassandra.thrift.ConsistencyLevel;
import org.apache.cassandra.thrift.InvalidRequestException;
+
+ import static org.apache.cassandra.cql.AlterTableStatement.OperationType;
}
@members {
@@ -113,6 +115,7 @@ query returns [CQLStatement stmnt]
| createIndexStatement { $stmnt = new CQLStatement(StatementType.CREATE_INDEX, $createIndexStatement.expr); }
| dropKeyspaceStatement { $stmnt = new CQLStatement(StatementType.DROP_KEYSPACE, $dropKeyspaceStatement.ksp); }
| dropColumnFamilyStatement { $stmnt = new CQLStatement(StatementType.DROP_COLUMNFAMILY, $dropColumnFamilyStatement.cfam); }
+ | alterTableStatement { $stmnt = new CQLStatement(StatementType.ALTER_TABLE, $alterTableStatement.expr); }
;
// USE <KEYSPACE>;
@@ -386,6 +389,27 @@ dropKeyspaceStatement returns [String ks
: K_DROP K_KEYSPACE name=( IDENT | STRING_LITERAL | INTEGER ) endStmnt { $ksp = $name.text; }
;
+
+alterTableStatement returns [AlterTableStatement expr]
+ :
+ {
+ OperationType type = null;
+ String columnFamily = null, columnName = null, validator = null;
+ }
+ K_ALTER K_TABLE name=( IDENT | STRING_LITERAL | INTEGER ) { columnFamily = $name.text; }
+ ( K_ALTER { type = OperationType.ALTER; }
+ (col=( IDENT | STRING_LITERAL | INTEGER ) { columnName = $col.text; })
+ K_TYPE alterValidator=comparatorType { validator = $alterValidator.text; }
+ | K_ADD { type = OperationType.ADD; }
+ (col=( IDENT | STRING_LITERAL | INTEGER ) { columnName = $col.text; })
+ addValidator=comparatorType { validator = $addValidator.text; }
+ | K_DROP { type = OperationType.DROP; }
+ (col=( IDENT | STRING_LITERAL | INTEGER ) { columnName = $col.text; }))
+ endStmnt
+ {
+ $expr = new AlterTableStatement(columnFamily, type, columnName, validator);
+ }
+ ;
/** DROP COLUMNFAMILY <CF>; */
dropColumnFamilyStatement returns [String cfam]
: K_DROP K_COLUMNFAMILY name=( IDENT | STRING_LITERAL | INTEGER ) endStmnt { $cfam = $name.text; }
@@ -468,6 +492,10 @@ K_INTO: I N T O;
K_VALUES: V A L U E S;
K_TIMESTAMP: T I M E S T A M P;
K_TTL: T T L;
+K_ALTER: A L T E R;
+K_TABLE: T A B L E;
+K_ADD: A D D;
+K_TYPE: T Y P E;
// Case-insensitive alpha characters
fragment A: ('a'|'A');
Modified: cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java?rev=1130200&r1=1130199&r2=1130200&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java (original)
+++ cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java Wed Jun 1 15:58:44 2011
@@ -58,7 +58,7 @@ public class CreateColumnFamilyStatement
private static final String KW_ROW_CACHE_PROVIDER = "row_cache_provider";
// Maps CQL short names to the respective Cassandra comparator/validator class names
- private static final Map<String, String> comparators = new HashMap<String, String>();
+ public static final Map<String, String> comparators = new HashMap<String, String>();
private static final Set<String> keywords = new HashSet<String>();
static
Modified: cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java?rev=1130200&r1=1130199&r2=1130200&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java (original)
+++ cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java Wed Jun 1 15:58:44 2011
@@ -48,7 +48,6 @@ import org.apache.cassandra.db.migration
import org.apache.cassandra.db.migration.avro.CfDef;
import org.apache.cassandra.dht.*;
import org.apache.cassandra.dht.Token;
-import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.service.StorageService;
@@ -791,7 +790,35 @@ public class QueryProcessor
result.type = CqlResultType.VOID;
return result;
-
+
+ case ALTER_TABLE:
+ AlterTableStatement alterTable = (AlterTableStatement) statement.statement;
+
+ System.out.println(alterTable);
+
+ validateColumnFamily(keyspace, alterTable.columnFamily);
+ clientState.hasColumnFamilyAccess(alterTable.columnFamily, Permission.WRITE);
+ validateSchemaAgreement();
+
+ try
+ {
+ applyMigrationOnStage(new UpdateColumnFamily(alterTable.getCfDef(keyspace)));
+ }
+ catch (ConfigurationException e)
+ {
+ InvalidRequestException ex = new InvalidRequestException(e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ }
+ catch (IOException e)
+ {
+ InvalidRequestException ex = new InvalidRequestException(e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ }
+
+ result.type = CqlResultType.VOID;
+ return result;
}
return null; // We should never get here.
Modified: cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/StatementType.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/StatementType.java?rev=1130200&r1=1130199&r2=1130200&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/StatementType.java (original)
+++ cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/StatementType.java Wed Jun 1 15:58:44 2011
@@ -25,7 +25,7 @@ import java.util.EnumSet;
public enum StatementType
{
SELECT, INSERT, UPDATE, BATCH, USE, TRUNCATE, DELETE, CREATE_KEYSPACE, CREATE_COLUMNFAMILY, CREATE_INDEX,
- DROP_KEYSPACE, DROP_COLUMNFAMILY;
+ DROP_KEYSPACE, DROP_COLUMNFAMILY, ALTER_TABLE;
// Statement types that don't require a keyspace to be set.
private static final EnumSet<StatementType> topLevel = EnumSet.of(USE, CREATE_KEYSPACE, DROP_KEYSPACE);
Modified: cassandra/branches/cassandra-0.8/test/system/test_cql.py
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/test/system/test_cql.py?rev=1130200&r1=1130199&r2=1130200&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/test/system/test_cql.py (original)
+++ cassandra/branches/cassandra-0.8/test/system/test_cql.py Wed Jun 1 15:58:44 2011
@@ -1006,3 +1006,78 @@ class TestCql(ThriftTester):
r = cursor.fetchone()
assert len(r) == 1, "expected 0 results, got %d" % len(r)
+
+ def test_alter_table_statement(self):
+ "test ALTER TABLE statement"
+ cursor = init()
+ cursor.execute("""
+ CREATE KEYSPACE AlterTableKS WITH strategy_options:replication_factor = '1'
+ AND strategy_class = 'SimpleStrategy';
+ """)
+ cursor.execute("USE AlterTableKS;")
+
+ cursor.execute("""
+ CREATE COLUMNFAMILY NewCf1 (KEY varint PRIMARY KEY) WITH default_validation = ascii;
+ """)
+
+ # TODO: temporary (until this can be done with CQL).
+ ksdef = thrift_client.describe_keyspace("AlterTableKS")
+ assert len(ksdef.cf_defs) == 1, \
+ "expected 1 column family total, found %d" % len(ksdef.cf_defs)
+ cfam = ksdef.cf_defs[0]
+
+ assert len(cfam.column_metadata) == 0
+
+ # testing "add a new column"
+ cursor.execute("ALTER TABLE NewCf1 ADD name varchar")
+
+ ksdef = thrift_client.describe_keyspace("AlterTableKS")
+ assert len(ksdef.cf_defs) == 1, \
+ "expected 1 column family total, found %d" % len(ksdef.cf_defs)
+ columns = ksdef.cf_defs[0].column_metadata
+
+ assert len(columns) == 1
+ assert columns[0].name == 'name'
+ assert columns[0].validation_class == 'org.apache.cassandra.db.marshal.UTF8Type'
+
+ # testing "alter a column type"
+ cursor.execute("ALTER TABLE NewCf1 ALTER name TYPE ascii")
+
+ ksdef = thrift_client.describe_keyspace("AlterTableKS")
+ assert len(ksdef.cf_defs) == 1, \
+ "expected 1 column family total, found %d" % len(ksdef.cf_defs)
+ columns = ksdef.cf_defs[0].column_metadata
+
+ assert len(columns) == 1
+ assert columns[0].name == 'name'
+ assert columns[0].validation_class == 'org.apache.cassandra.db.marshal.AsciiType'
+
+ # alter column with unknown validator
+ assert_raises(cql.ProgrammingError,
+ cursor.execute,
+ "ALTER TABLE NewCf1 ADD name utf8")
+
+ # testing 'drop an existing column'
+ cursor.execute("ALTER TABLE NewCf1 DROP name")
+
+ ksdef = thrift_client.describe_keyspace("AlterTableKS")
+ assert len(ksdef.cf_defs) == 1, \
+ "expected 1 column family total, found %d" % len(ksdef.cf_defs)
+ columns = ksdef.cf_defs[0].column_metadata
+
+ assert len(columns) == 0
+
+ # add column with unknown validator
+ assert_raises(cql.ProgrammingError,
+ cursor.execute,
+ "ALTER TABLE NewCf1 ADD name utf8")
+
+ # alter not existing column
+ assert_raises(cql.ProgrammingError,
+ cursor.execute,
+ "ALTER TABLE NewCf1 ALTER name TYPE uuid")
+
+ # drop not existing column
+ assert_raises(cql.ProgrammingError,
+ cursor.execute,
+ "ALTER TABLE NewCf1 DROP name")