Merge branch 'cassandra-2.0' into cassandra-2.1

Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/664efd41
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/664efd41
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/664efd41

Branch: refs/heads/trunk
Commit: 664efd41b037d17a7e2c991a9ad660c5d2ec2ce5
Parents: d088f02 e4d5eda
Author: Tyler Hobbs <ty...@datastax.com>
Authored: Thu Aug 21 14:23:07 2014 -0500
Committer: Tyler Hobbs <ty...@datastax.com>
Committed: Thu Aug 21 14:23:07 2014 -0500

----------------------------------------------------------------------
 CHANGES.txt                                       |  2 ++
 .../cql3/statements/AlterKeyspaceStatement.java   |  3 ++-
 .../cql3/statements/AlterTableStatement.java      |  3 ++-
 .../cql3/statements/AlterTypeStatement.java       |  3 ++-
 .../cql3/statements/CreateIndexStatement.java     |  5 +++--
 .../cql3/statements/CreateKeyspaceStatement.java  |  8 +++++---
 .../cql3/statements/CreateTableStatement.java     | 10 ++++++----
 .../cql3/statements/CreateTriggerStatement.java   |  3 ++-
 .../cql3/statements/CreateTypeStatement.java      |  5 +++--
 .../cql3/statements/DropIndexStatement.java       |  5 +++--
 .../cql3/statements/DropKeyspaceStatement.java    |  8 +++++---
 .../cql3/statements/DropTableStatement.java       |  8 +++++---
 .../cql3/statements/DropTriggerStatement.java     |  3 ++-
 .../cql3/statements/DropTypeStatement.java        |  9 ++++++---
 .../cql3/statements/SchemaAlteringStatement.java  | 18 +++++++++++++-----
 15 files changed, 61 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 701fd38,9aeeb29..80eb279
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,52 -1,7 +1,54 @@@
 -2.0.10
 +2.1.1
 + * (cqlsh) Order UDTs according to cross-type dependencies in DESCRIBE
 +   output (CASSANDRA-7659)
 + * (cqlsh) Fix handling of CAS statement results (CASSANDRA-7671)
 + * (cqlsh) COPY TO/FROM improvements (CASSANDRA-7405)
 + * Support list index operations with conditions (CASSANDRA-7499)
 + * Add max live/tombstoned cells to nodetool cfstats output (CASSANDRA-7731)
 + * Validate IPv6 wildcard addresses properly (CASSANDRA-7680)
 + * (cqlsh) Error when tracing query (CASSANDRA-7613)
 + * Avoid IOOBE when building SyntaxError message snippet (CASSANDRA-7569)
 + * SSTableExport uses correct validator to create string representation of 
partition
 +   keys (CASSANDRA-7498)
 + * Avoid NPEs when receiving type changes for an unknown keyspace 
(CASSANDRA-7689)
 + * Add support for custom 2i validation (CASSANDRA-7575)
 + * Pig support for hadoop CqlInputFormat (CASSANDRA-6454)
 + * Add listen_interface and rpc_interface options (CASSANDRA-7417)
 + * Improve schema merge performance (CASSANDRA-7444)
 + * Adjust MT depth based on # of partition validating (CASSANDRA-5263)
 + * Optimise NativeCell comparisons (CASSANDRA-6755)
 + * Configurable client timeout for cqlsh (CASSANDRA-7516)
 + * Include snippet of CQL query near syntax error in messages (CASSANDRA-7111)
 +Merged from 2.0:
+  * Don't send schema change responses and events for no-op DDL
+    statements (CASSANDRA-7600)
   * (Hadoop) fix cluster initialisation for a split fetching (CASSANDRA-7774)
 + * Throw InvalidRequestException when queries contain relations on entire
 +   collection columns (CASSANDRA-7506)
 + * (cqlsh) enable CTRL-R history search with libedit (CASSANDRA-7577)
 + * (Hadoop) allow ACFRW to limit nodes to local DC (CASSANDRA-7252)
 + * (cqlsh) cqlsh should automatically disable tracing when selecting
 +   from system_traces (CASSANDRA-7641)
 + * (Hadoop) Add CqlOutputFormat (CASSANDRA-6927)
 + * Don't depend on cassandra config for nodetool ring (CASSANDRA-7508)
 + * (cqlsh) Fix failing cqlsh formatting tests (CASSANDRA-7703)
 + * Fix IncompatibleClassChangeError from hadoop2 (CASSANDRA-7229)
 + * Add 'nodetool sethintedhandoffthrottlekb' (CASSANDRA-7635)
 + * (cqlsh) Add tab-completion for CREATE/DROP USER IF [NOT] EXISTS 
(CASSANDRA-7611)
 + * Catch errors when the JVM pulls the rug out from GCInspector 
(CASSANDRA-5345)
 + * cqlsh fails when version number parts are not int (CASSANDRA-7524)
 +Merged from 1.2:
 + * Improve PasswordAuthenticator default super user setup (CASSANDRA-7788)
 +
 +
 +2.1.0
 + * Correctly remove tmplink files (CASSANDRA-7803)
 + * (cqlsh) Fix column name formatting for functions, CAS operations,
 +   and UDT field selections (CASSANDRA-7806)
 + * (cqlsh) Fix COPY FROM handling of null/empty primary key
 +   values (CASSANDRA-7792)
 + * Fix ordering of static cells (CASSANDRA-7763)
 +Merged from 2.0:
   * Configure system.paxos with LeveledCompactionStrategy (CASSANDRA-7753)
   * Fix ALTER clustering column type from DateType to TimestampType when
     using DESC clustering order (CASSANRDA-7797)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java
index 27cda49,4f6d1f2..e65a51e
--- a/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java
@@@ -79,18 -79,19 +79,19 @@@ public class AlterKeyspaceStatement ext
          }
      }
  
-     public void announceMigration(boolean isLocalOnly) throws 
RequestValidationException
 -    public boolean announceMigration() throws RequestValidationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
RequestValidationException
      {
          KSMetaData ksm = Schema.instance.getKSMetaData(name);
          // In the (very) unlikely case the keyspace was dropped since 
validate()
          if (ksm == null)
              throw new InvalidRequestException("Unknown keyspace " + name);
  
 -        
MigrationManager.announceKeyspaceUpdate(attrs.asKSMetadataUpdate(ksm));
 +        
MigrationManager.announceKeyspaceUpdate(attrs.asKSMetadataUpdate(ksm), 
isLocalOnly);
+         return true;
      }
  
 -    public ResultMessage.SchemaChange.Change changeType()
 +    public Event.SchemaChange changeEvent()
      {
 -        return ResultMessage.SchemaChange.Change.UPDATED;
 +        return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, 
keyspace());
      }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
index be28943,dfcd601..3005ac7
--- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
@@@ -74,14 -76,13 +74,14 @@@ public class AlterTableStatement extend
          // validated in announceMigration()
      }
  
-     public void announceMigration(boolean isLocalOnly) throws 
RequestValidationException
 -    public boolean announceMigration() throws RequestValidationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
RequestValidationException
      {
          CFMetaData meta = validateColumnFamily(keyspace(), columnFamily());
 -        CFMetaData cfm = meta.clone();
 +        CFMetaData cfm = meta.copy();
  
 -        CFDefinition cfDef = meta.getCfDef();
 -        CFDefinition.Name name = columnName == null ? null : 
cfDef.get(columnName);
 +        CQL3Type validator = this.validator == null ? null : 
this.validator.prepare(keyspace());
 +
 +        ColumnDefinition def = columnName == null ? null : 
cfm.getColumnDefinition(columnName);
          switch (oType)
          {
              case ADD:
@@@ -256,7 -265,8 +256,8 @@@
                  break;
          }
  
 -        MigrationManager.announceColumnFamilyUpdate(cfm, false);
 +        MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly);
+         return true;
      }
  
      public String toString()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java
index 94f7c87,0000000..cfdd65f
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java
@@@ -1,343 -1,0 +1,344 @@@
 +/*
 + * 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 java.nio.ByteBuffer;
 +import java.util.*;
 +
 +import org.apache.cassandra.auth.Permission;
 +import org.apache.cassandra.config.*;
 +import org.apache.cassandra.cql3.*;
 +import org.apache.cassandra.db.composites.CellNames;
 +import org.apache.cassandra.db.marshal.*;
 +import org.apache.cassandra.exceptions.*;
 +import org.apache.cassandra.service.ClientState;
 +import org.apache.cassandra.service.MigrationManager;
 +import org.apache.cassandra.transport.Event;
 +
 +public abstract class AlterTypeStatement extends SchemaAlteringStatement
 +{
 +    protected final UTName name;
 +
 +    protected AlterTypeStatement(UTName name)
 +    {
 +        super();
 +        this.name = name;
 +    }
 +
 +    @Override
 +    public void prepareKeyspace(ClientState state) throws 
InvalidRequestException
 +    {
 +        if (!name.hasKeyspace())
 +            name.setKeyspace(state.getKeyspace());
 +
 +        if (name.getKeyspace() == null)
 +            throw new InvalidRequestException("You need to be logged in a 
keyspace or use a fully qualified user type name");
 +    }
 +
 +    protected abstract UserType makeUpdatedType(UserType toUpdate) throws 
InvalidRequestException;
 +
 +    public static AlterTypeStatement addition(UTName name, ColumnIdentifier 
fieldName, CQL3Type.Raw type)
 +    {
 +        return new AddOrAlter(name, true, fieldName, type);
 +    }
 +
 +    public static AlterTypeStatement alter(UTName name, ColumnIdentifier 
fieldName, CQL3Type.Raw type)
 +    {
 +        return new AddOrAlter(name, false, fieldName, type);
 +    }
 +
 +    public static AlterTypeStatement renames(UTName name, 
Map<ColumnIdentifier, ColumnIdentifier> renames)
 +    {
 +        return new Renames(name, renames);
 +    }
 +
 +    public void checkAccess(ClientState state) throws UnauthorizedException, 
InvalidRequestException
 +    {
 +        state.hasKeyspaceAccess(keyspace(), Permission.ALTER);
 +    }
 +
 +    public void validate(ClientState state) throws RequestValidationException
 +    {
 +        // Validation is left to announceMigration as it's easier to do it 
while constructing the updated type.
 +        // It doesn't really change anything anyway.
 +    }
 +
 +    public Event.SchemaChange changeEvent()
 +    {
 +        return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, 
Event.SchemaChange.Target.TYPE, keyspace(), name.getStringTypeName());
 +    }
 +
 +    @Override
 +    public String keyspace()
 +    {
 +        return name.getKeyspace();
 +    }
 +
-     public void announceMigration(boolean isLocalOnly) throws 
InvalidRequestException, ConfigurationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
InvalidRequestException, ConfigurationException
 +    {
 +        KSMetaData ksm = Schema.instance.getKSMetaData(name.getKeyspace());
 +        if (ksm == null)
 +            throw new InvalidRequestException(String.format("Cannot alter 
type in unknown keyspace %s", name.getKeyspace()));
 +
 +        UserType toUpdate = ksm.userTypes.getType(name.getUserTypeName());
 +        // Shouldn't happen, unless we race with a drop
 +        if (toUpdate == null)
 +            throw new InvalidRequestException(String.format("No user type 
named %s exists.", name));
 +
 +        UserType updated = makeUpdatedType(toUpdate);
 +
 +        // Now, we need to announce the type update to basically change it 
for new tables using this type,
 +        // but we also need to find all existing user types and CF using it 
and change them.
 +        MigrationManager.announceTypeUpdate(updated, isLocalOnly);
 +
 +        for (KSMetaData ksm2 : Schema.instance.getKeyspaceDefinitions())
 +        {
 +            for (CFMetaData cfm : ksm2.cfMetaData().values())
 +            {
 +                CFMetaData copy = cfm.copy();
 +                boolean modified = false;
 +                for (ColumnDefinition def : copy.allColumns())
 +                    modified |= updateDefinition(copy, def, 
toUpdate.keyspace, toUpdate.name, updated);
 +                if (modified)
 +                    MigrationManager.announceColumnFamilyUpdate(copy, false, 
isLocalOnly);
 +            }
 +
 +            // Other user types potentially using the updated type
 +            for (UserType ut : ksm2.userTypes.getAllTypes().values())
 +            {
 +                // Re-updating the type we've just updated would be harmless 
but useless so we avoid it.
 +                // Besides, we use the occasion to drop the old version of 
the type if it's a type rename
 +                if (ut.keyspace.equals(toUpdate.keyspace) && 
ut.name.equals(toUpdate.name))
 +                {
 +                    if (!ut.keyspace.equals(updated.keyspace) || 
!ut.name.equals(updated.name))
 +                        MigrationManager.announceTypeDrop(ut);
 +                    continue;
 +                }
 +                AbstractType<?> upd = updateWith(ut, toUpdate.keyspace, 
toUpdate.name, updated);
 +                if (upd != null)
 +                    MigrationManager.announceTypeUpdate((UserType)upd, 
isLocalOnly);
 +            }
 +        }
++        return true;
 +    }
 +
 +    private static int getIdxOfField(UserType type, ColumnIdentifier field)
 +    {
 +        for (int i = 0; i < type.size(); i++)
 +            if (field.bytes.equals(type.fieldName(i)))
 +                return i;
 +        return -1;
 +    }
 +
 +    private boolean updateDefinition(CFMetaData cfm, ColumnDefinition def, 
String keyspace, ByteBuffer toReplace, UserType updated)
 +    {
 +        AbstractType<?> t = updateWith(def.type, keyspace, toReplace, 
updated);
 +        if (t == null)
 +            return false;
 +
 +        // We need to update this validator ...
 +        cfm.addOrReplaceColumnDefinition(def.withNewType(t));
 +
 +        // ... but if it's part of the comparator or key validator, we need 
to go update those too.
 +        switch (def.kind)
 +        {
 +            case PARTITION_KEY:
 +                cfm.keyValidator(updateWith(cfm.getKeyValidator(), keyspace, 
toReplace, updated));
 +                break;
 +            case CLUSTERING_COLUMN:
 +                cfm.comparator = 
CellNames.fromAbstractType(updateWith(cfm.comparator.asAbstractType(), 
keyspace, toReplace, updated), cfm.comparator.isDense());
 +                break;
 +            default:
 +                // If it's a collection, we still want to modify the 
comparator because the collection is aliased in it
 +                if (def.type instanceof CollectionType)
 +                    cfm.comparator = 
CellNames.fromAbstractType(updateWith(cfm.comparator.asAbstractType(), 
keyspace, toReplace, updated), cfm.comparator.isDense());
 +        }
 +        return true;
 +    }
 +
 +    // Update the provided type were all instance of a given userType is 
replaced by a new version
 +    // Note that this methods reaches inside other UserType, CompositeType 
and CollectionType.
 +    private static AbstractType<?> updateWith(AbstractType<?> type, String 
keyspace, ByteBuffer toReplace, UserType updated)
 +    {
 +        if (type instanceof UserType)
 +        {
 +            UserType ut = (UserType)type;
 +
 +            // If it's directly the type we've updated, then just use the new 
one.
 +            if (keyspace.equals(ut.keyspace) && toReplace.equals(ut.name))
 +                return updated;
 +
 +            // Otherwise, check for nesting
 +            List<AbstractType<?>> updatedTypes = updateTypes(ut.fieldTypes(), 
keyspace, toReplace, updated);
 +            return updatedTypes == null ? null : new UserType(ut.keyspace, 
ut.name, new ArrayList<>(ut.fieldNames()), updatedTypes);
 +        }
 +        else if (type instanceof CompositeType)
 +        {
 +            CompositeType ct = (CompositeType)type;
 +            List<AbstractType<?>> updatedTypes = updateTypes(ct.types, 
keyspace, toReplace, updated);
 +            return updatedTypes == null ? null : 
CompositeType.getInstance(updatedTypes);
 +        }
 +        else if (type instanceof ColumnToCollectionType)
 +        {
 +            ColumnToCollectionType ctct = (ColumnToCollectionType)type;
 +            Map<ByteBuffer, CollectionType> updatedTypes = null;
 +            for (Map.Entry<ByteBuffer, CollectionType> entry : 
ctct.defined.entrySet())
 +            {
 +                AbstractType<?> t = updateWith(entry.getValue(), keyspace, 
toReplace, updated);
 +                if (t == null)
 +                    continue;
 +
 +                if (updatedTypes == null)
 +                    updatedTypes = new HashMap<>(ctct.defined);
 +
 +                updatedTypes.put(entry.getKey(), (CollectionType)t);
 +            }
 +            return updatedTypes == null ? null : 
ColumnToCollectionType.getInstance(updatedTypes);
 +        }
 +        else if (type instanceof CollectionType)
 +        {
 +            if (type instanceof ListType)
 +            {
 +                AbstractType<?> t = updateWith(((ListType)type).elements, 
keyspace, toReplace, updated);
 +                return t == null ? null : ListType.getInstance(t);
 +            }
 +            else if (type instanceof SetType)
 +            {
 +                AbstractType<?> t = updateWith(((SetType)type).elements, 
keyspace, toReplace, updated);
 +                return t == null ? null : SetType.getInstance(t);
 +            }
 +            else
 +            {
 +                assert type instanceof MapType;
 +                MapType mt = (MapType)type;
 +                AbstractType<?> k = updateWith(mt.keys, keyspace, toReplace, 
updated);
 +                AbstractType<?> v = updateWith(mt.values, keyspace, 
toReplace, updated);
 +                if (k == null && v == null)
 +                    return null;
 +                return MapType.getInstance(k == null ? mt.keys : k, v == null 
? mt.values : v);
 +            }
 +        }
 +        else
 +        {
 +            return null;
 +        }
 +    }
 +
 +    private static List<AbstractType<?>> updateTypes(List<AbstractType<?>> 
toUpdate, String keyspace, ByteBuffer toReplace, UserType updated)
 +    {
 +        // But this can also be nested.
 +        List<AbstractType<?>> updatedTypes = null;
 +        for (int i = 0; i < toUpdate.size(); i++)
 +        {
 +            AbstractType<?> t = updateWith(toUpdate.get(i), keyspace, 
toReplace, updated);
 +            if (t == null)
 +                continue;
 +
 +            if (updatedTypes == null)
 +                updatedTypes = new ArrayList<>(toUpdate);
 +
 +            updatedTypes.set(i, t);
 +        }
 +        return updatedTypes;
 +    }
 +
 +    private static class AddOrAlter extends AlterTypeStatement
 +    {
 +        private final boolean isAdd;
 +        private final ColumnIdentifier fieldName;
 +        private final CQL3Type.Raw type;
 +
 +        public AddOrAlter(UTName name, boolean isAdd, ColumnIdentifier 
fieldName, CQL3Type.Raw type)
 +        {
 +            super(name);
 +            this.isAdd = isAdd;
 +            this.fieldName = fieldName;
 +            this.type = type;
 +        }
 +
 +        private UserType doAdd(UserType toUpdate) throws 
InvalidRequestException
 +        {
 +            if (getIdxOfField(toUpdate, fieldName) >= 0)
 +                throw new InvalidRequestException(String.format("Cannot add 
new field %s to type %s: a field of the same name already exists", fieldName, 
name));
 +
 +            List<ByteBuffer> newNames = new ArrayList<>(toUpdate.size() + 1);
 +            newNames.addAll(toUpdate.fieldNames());
 +            newNames.add(fieldName.bytes);
 +
 +            List<AbstractType<?>> newTypes = new ArrayList<>(toUpdate.size() 
+ 1);
 +            newTypes.addAll(toUpdate.fieldTypes());
 +            newTypes.add(type.prepare(keyspace()).getType());
 +
 +            return new UserType(toUpdate.keyspace, toUpdate.name, newNames, 
newTypes);
 +        }
 +
 +        private UserType doAlter(UserType toUpdate) throws 
InvalidRequestException
 +        {
 +            int idx = getIdxOfField(toUpdate, fieldName);
 +            if (idx < 0)
 +                throw new InvalidRequestException(String.format("Unknown 
field %s in type %s", fieldName, name));
 +
 +            AbstractType<?> previous = toUpdate.fieldType(idx);
 +            if 
(!type.prepare(keyspace()).getType().isCompatibleWith(previous))
 +                throw new InvalidRequestException(String.format("Type %s is 
incompatible with previous type %s of field %s in user type %s", type, 
previous.asCQL3Type(), fieldName, name));
 +
 +            List<ByteBuffer> newNames = new 
ArrayList<>(toUpdate.fieldNames());
 +            List<AbstractType<?>> newTypes = new 
ArrayList<>(toUpdate.fieldTypes());
 +            newTypes.set(idx, type.prepare(keyspace()).getType());
 +
 +            return new UserType(toUpdate.keyspace, toUpdate.name, newNames, 
newTypes);
 +        }
 +
 +        protected UserType makeUpdatedType(UserType toUpdate) throws 
InvalidRequestException
 +        {
 +            return isAdd ? doAdd(toUpdate) : doAlter(toUpdate);
 +        }
 +    }
 +
 +    private static class Renames extends AlterTypeStatement
 +    {
 +        private final Map<ColumnIdentifier, ColumnIdentifier> renames;
 +
 +        public Renames(UTName name, Map<ColumnIdentifier, ColumnIdentifier> 
renames)
 +        {
 +            super(name);
 +            this.renames = renames;
 +        }
 +
 +        protected UserType makeUpdatedType(UserType toUpdate) throws 
InvalidRequestException
 +        {
 +            List<ByteBuffer> newNames = new 
ArrayList<>(toUpdate.fieldNames());
 +            List<AbstractType<?>> newTypes = new 
ArrayList<>(toUpdate.fieldTypes());
 +
 +            for (Map.Entry<ColumnIdentifier, ColumnIdentifier> entry : 
renames.entrySet())
 +            {
 +                ColumnIdentifier from = entry.getKey();
 +                ColumnIdentifier to = entry.getValue();
 +                int idx = getIdxOfField(toUpdate, from);
 +                if (idx < 0)
 +                    throw new InvalidRequestException(String.format("Unknown 
field %s in type %s", from, name));
 +                newNames.set(idx, to.bytes);
 +            }
 +
 +            UserType updated = new UserType(toUpdate.keyspace, toUpdate.name, 
newNames, newTypes);
 +            CreateTypeStatement.checkForDuplicateNames(updated);
 +            return updated;
 +        }
 +
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
index 3f2635f,8b40978..4809187
--- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
@@@ -110,49 -93,41 +110,50 @@@ public class CreateIndexStatement exten
          // would pull the full partition every time the static column of 
partition is 'bar', which sounds like offering a
          // fair potential for foot-shooting, so I prefer leaving that to a 
follow up ticket once we have identified cases where
          // such indexing is actually useful.
 -        if (cd.type == ColumnDefinition.Type.STATIC)
 +        if (cd.isStatic())
              throw new InvalidRequestException("Secondary indexes are not 
allowed on static columns");
  
 -        if (cd.getValidator().isCollection() && !properties.isCustom)
 -            throw new InvalidRequestException("Indexes on collections are no 
yet supported");
 -
 -        if (cd.type == ColumnDefinition.Type.PARTITION_KEY && 
cd.componentIndex == null)
 -            throw new InvalidRequestException(String.format("Cannot add 
secondary index to already primarily indexed column %s", columnName));
 +        if (cd.kind == ColumnDefinition.Kind.PARTITION_KEY && 
cd.isOnAllComponents())
 +            throw new InvalidRequestException(String.format("Cannot add 
secondary index to already primarily indexed column %s", target.column));
      }
  
-     public void announceMigration(boolean isLocalOnly) throws 
RequestValidationException
 -    public boolean announceMigration() throws RequestValidationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
RequestValidationException
      {
 -        logger.debug("Updating column {} definition for index {}", 
columnName, indexName);
 -        CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), 
columnFamily()).clone();
 -        ColumnDefinition cd = cfm.getColumnDefinition(columnName.key);
 +        logger.debug("Updating column {} definition for index {}", 
target.column, indexName);
 +        CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), 
columnFamily()).copy();
 +        ColumnDefinition cd = cfm.getColumnDefinition(target.column);
  
          if (cd.getIndexType() != null && ifNotExists)
-             return;
+             return false;
  
          if (properties.isCustom)
 +        {
              cd.setIndexType(IndexType.CUSTOM, properties.getOptions());
 -        else if (cfm.getCfDef().isComposite)
 -            cd.setIndexType(IndexType.COMPOSITES, Collections.<String, 
String>emptyMap());
 +        }
 +        else if (cfm.comparator.isCompound())
 +        {
 +            Map<String, String> options = Collections.emptyMap();
 +            // For now, we only allow indexing values for collections, but we 
could later allow
 +            // to also index map keys, so we record that this is the values 
we index to make our
 +            // lives easier then.
 +            if (cd.type.isCollection())
 +                options = ImmutableMap.of(target.isCollectionKeys ? 
"index_keys" : "index_values", "");
 +            cd.setIndexType(IndexType.COMPOSITES, options);
 +        }
          else
 +        {
              cd.setIndexType(IndexType.KEYS, Collections.<String, 
String>emptyMap());
 +        }
  
          cd.setIndexName(indexName);
          cfm.addDefaultIndexNames();
 -        MigrationManager.announceColumnFamilyUpdate(cfm, false);
 +        MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly);
+         return true;
      }
  
 -    public ResultMessage.SchemaChange.Change changeType()
 +    public Event.SchemaChange changeEvent()
      {
          // Creating an index is akin to updating the CF
 -        return ResultMessage.SchemaChange.Change.UPDATED;
 +        return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, 
Event.SchemaChange.Target.TABLE, keyspace(), columnFamily());
      }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
index 78263b6,7a8473a..8281cbd
--- a/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
@@@ -97,11 -97,12 +97,12 @@@ public class CreateKeyspaceStatement ex
                                                                  
attrs.getReplicationOptions());
      }
  
-     public void announceMigration(boolean isLocalOnly) throws 
RequestValidationException
 -    public boolean announceMigration() throws RequestValidationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
RequestValidationException
      {
          try
          {
 -            MigrationManager.announceNewKeyspace(attrs.asKSMetadata(name));
 +            MigrationManager.announceNewKeyspace(attrs.asKSMetadata(name), 
isLocalOnly);
+             return true;
          }
          catch (AlreadyExistsException e)
          {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
index 47f05bb,b7f43d3..891a895
--- a/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
@@@ -107,11 -114,12 +107,12 @@@ public class CreateTableStatement exten
          return columnDefs;
      }
  
-     public void announceMigration(boolean isLocalOnly) throws 
RequestValidationException
 -    public boolean announceMigration() throws RequestValidationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
RequestValidationException
      {
          try
          {
-            MigrationManager.announceNewColumnFamily(getCFMetaData(), 
isLocalOnly);
 -            MigrationManager.announceNewColumnFamily(getCFMetaData());
++            MigrationManager.announceNewColumnFamily(getCFMetaData(), 
isLocalOnly);
+             return true;
          }
          catch (AlreadyExistsException e)
          {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java
index 9b7313f,70b3acb..db0cc22
--- a/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java
@@@ -65,16 -65,17 +65,17 @@@ public class CreateTriggerStatement ext
          }
      }
  
-     public void announceMigration(boolean isLocalOnly) throws 
ConfigurationException
 -    public boolean announceMigration() throws ConfigurationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
ConfigurationException
      {
 -        CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), 
columnFamily()).clone();
 +        CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), 
columnFamily()).copy();
          cfm.addTriggerDefinition(TriggerDefinition.create(triggerName, 
triggerClass));
          logger.info("Adding trigger with name {} and class {}", triggerName, 
triggerClass);
 -        MigrationManager.announceColumnFamilyUpdate(cfm, false);
 +        MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly);
+         return true;
      }
  
 -    public ResultMessage.SchemaChange.Change changeType()
 +    public Event.SchemaChange changeEvent()
      {
 -        return ResultMessage.SchemaChange.Change.UPDATED;
 +        return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, 
Event.SchemaChange.Target.TABLE, keyspace(), columnFamily());
      }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/CreateTypeStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/CreateTypeStatement.java
index 5224474,0000000..82c2808
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateTypeStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateTypeStatement.java
@@@ -1,132 -1,0 +1,133 @@@
 +/*
 + * 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 java.nio.ByteBuffer;
 +import java.util.*;
 +
 +import org.apache.cassandra.auth.Permission;
 +import org.apache.cassandra.config.*;
 +import org.apache.cassandra.cql3.*;
 +import org.apache.cassandra.db.marshal.AbstractType;
 +import org.apache.cassandra.db.marshal.UTF8Type;
 +import org.apache.cassandra.db.marshal.UserType;
 +import org.apache.cassandra.exceptions.*;
 +import org.apache.cassandra.service.ClientState;
 +import org.apache.cassandra.service.MigrationManager;
 +import org.apache.cassandra.transport.Event;
 +
 +public class CreateTypeStatement extends SchemaAlteringStatement
 +{
 +    private final UTName name;
 +    private final List<ColumnIdentifier> columnNames = new ArrayList<>();
 +    private final List<CQL3Type.Raw> columnTypes = new ArrayList<>();
 +    private final boolean ifNotExists;
 +
 +    public CreateTypeStatement(UTName name, boolean ifNotExists)
 +    {
 +        super();
 +        this.name = name;
 +        this.ifNotExists = ifNotExists;
 +    }
 +
 +    @Override
 +    public void prepareKeyspace(ClientState state) throws 
InvalidRequestException
 +    {
 +        if (!name.hasKeyspace())
 +            name.setKeyspace(state.getKeyspace());
 +    }
 +
 +    public void addDefinition(ColumnIdentifier name, CQL3Type.Raw type)
 +    {
 +        columnNames.add(name);
 +        columnTypes.add(type);
 +    }
 +
 +    public void checkAccess(ClientState state) throws UnauthorizedException, 
InvalidRequestException
 +    {
 +        state.hasKeyspaceAccess(keyspace(), Permission.CREATE);
 +    }
 +
 +    public void validate(ClientState state) throws RequestValidationException
 +    {
 +        KSMetaData ksm = Schema.instance.getKSMetaData(name.getKeyspace());
 +        if (ksm == null)
 +            throw new InvalidRequestException(String.format("Cannot add type 
in unknown keyspace %s", name.getKeyspace()));
 +
 +        if (ksm.userTypes.getType(name.getUserTypeName()) != null && 
!ifNotExists)
 +            throw new InvalidRequestException(String.format("A user type of 
name %s already exists", name));
 +
 +        for (CQL3Type.Raw type : columnTypes)
 +            if (type.isCounter())
 +                throw new InvalidRequestException("A user type cannot contain 
counters");
 +    }
 +
 +    public static void checkForDuplicateNames(UserType type) throws 
InvalidRequestException
 +    {
 +        for (int i = 0; i < type.size() - 1; i++)
 +        {
 +            ByteBuffer fieldName = type.fieldName(i);
 +            for (int j = i+1; j < type.size(); j++)
 +            {
 +                if (fieldName.equals(type.fieldName(j)))
 +                    throw new 
InvalidRequestException(String.format("Duplicate field name %s in type %s",
 +                                                                    
UTF8Type.instance.getString(fieldName),
 +                                                                    
UTF8Type.instance.getString(type.name)));
 +            }
 +        }
 +    }
 +
 +    public Event.SchemaChange changeEvent()
 +    {
 +        return new Event.SchemaChange(Event.SchemaChange.Change.CREATED, 
Event.SchemaChange.Target.TYPE, keyspace(), name.getStringTypeName());
 +    }
 +
 +    @Override
 +    public String keyspace()
 +    {
 +        return name.getKeyspace();
 +    }
 +
 +    private UserType createType() throws InvalidRequestException
 +    {
 +        List<ByteBuffer> names = new ArrayList<>(columnNames.size());
 +        for (ColumnIdentifier name : columnNames)
 +            names.add(name.bytes);
 +
 +        List<AbstractType<?>> types = new ArrayList<>(columnTypes.size());
 +        for (CQL3Type.Raw type : columnTypes)
 +            types.add(type.prepare(keyspace()).getType());
 +
 +        return new UserType(name.getKeyspace(), name.getUserTypeName(), 
names, types);
 +    }
 +
-     public void announceMigration(boolean isLocalOnly) throws 
InvalidRequestException, ConfigurationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
InvalidRequestException, ConfigurationException
 +    {
 +        KSMetaData ksm = Schema.instance.getKSMetaData(name.getKeyspace());
 +        assert ksm != null; // should haven't validate otherwise
 +
 +        // Can happen with ifNotExists
 +        if (ksm.userTypes.getType(name.getUserTypeName()) != null)
-             return;
++            return false;
 +
 +        UserType type = createType();
 +        checkForDuplicateNames(type);
 +        MigrationManager.announceNewType(type, isLocalOnly);
++        return true;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java
index f70f526,ac5262e..5df8188
--- a/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java
@@@ -60,28 -54,21 +60,29 @@@ public class DropIndexStatement extend
          // validated in findIndexedCf()
      }
  
 -    public ResultMessage.SchemaChange.Change changeType()
 +    public Event.SchemaChange changeEvent()
      {
          // Dropping an index is akin to updating the CF
 -        return ResultMessage.SchemaChange.Change.UPDATED;
 +        return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, 
Event.SchemaChange.Target.TABLE, keyspace(), columnFamily());
 +    }
 +
 +    @Override
 +    public ResultMessage execute(QueryState state, QueryOptions options) 
throws RequestValidationException
 +    {
 +        announceMigration(false);
 +        return indexedCF == null ? null : new 
ResultMessage.SchemaChange(changeEvent());
      }
  
-     public void announceMigration(boolean isLocalOnly) throws 
InvalidRequestException, ConfigurationException
 -    public boolean announceMigration() throws InvalidRequestException, 
ConfigurationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
InvalidRequestException, ConfigurationException
      {
          CFMetaData cfm = findIndexedCF();
          if (cfm == null)
-             return;
+             return false;
  
          CFMetaData updatedCfm = updateCFMetadata(cfm);
 -        MigrationManager.announceColumnFamilyUpdate(updatedCfm, false);
 +        indexedCF = updatedCfm.cfName;
 +        MigrationManager.announceColumnFamilyUpdate(updatedCfm, false, 
isLocalOnly);
+         return true;
      }
  
      private CFMetaData updateCFMetadata(CFMetaData cfm)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/DropKeyspaceStatement.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/statements/DropKeyspaceStatement.java
index 0a3a510,7582af0..ba6b917
--- a/src/java/org/apache/cassandra/cql3/statements/DropKeyspaceStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropKeyspaceStatement.java
@@@ -55,11 -55,12 +55,12 @@@ public class DropKeyspaceStatement exte
          return keyspace;
      }
  
-     public void announceMigration(boolean isLocalOnly) throws 
ConfigurationException
 -    public boolean announceMigration() throws ConfigurationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
ConfigurationException
      {
          try
          {
 -            MigrationManager.announceKeyspaceDrop(keyspace);
 +            MigrationManager.announceKeyspaceDrop(keyspace, isLocalOnly);
+             return true;
          }
          catch(ConfigurationException e)
          {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/DropTableStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/DropTableStatement.java
index 49979b1,65a3f14..e690c3e
--- a/src/java/org/apache/cassandra/cql3/statements/DropTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropTableStatement.java
@@@ -54,11 -54,12 +54,12 @@@ public class DropTableStatement extend
          // validated in announceMigration()
      }
  
-     public void announceMigration(boolean isLocalOnly) throws 
ConfigurationException
 -    public boolean announceMigration() throws ConfigurationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
ConfigurationException
      {
          try
          {
 -            MigrationManager.announceColumnFamilyDrop(keyspace(), 
columnFamily());
 +            MigrationManager.announceColumnFamilyDrop(keyspace(), 
columnFamily(), isLocalOnly);
+             return true;
          }
          catch (ConfigurationException e)
          {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java
index 594aeac,f0bd637..4fdc21e
--- a/src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropTriggerStatement.java
@@@ -53,17 -53,18 +53,18 @@@ public class DropTriggerStatement exten
          ThriftValidation.validateColumnFamily(keyspace(), columnFamily());
      }
  
-     public void announceMigration(boolean isLocalOnly) throws 
ConfigurationException
 -    public boolean announceMigration() throws ConfigurationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
ConfigurationException
      {
 -        CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), 
columnFamily()).clone();
 +        CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), 
columnFamily()).copy();
          if (!cfm.removeTrigger(triggerName))
              throw new ConfigurationException(String.format("Trigger %s was 
not found", triggerName));
          logger.info("Dropping trigger with name {}", triggerName);
 -        MigrationManager.announceColumnFamilyUpdate(cfm, false);
 +        MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly);
+         return true;
      }
  
 -    public ResultMessage.SchemaChange.Change changeType()
 +    public Event.SchemaChange changeEvent()
      {
 -        return ResultMessage.SchemaChange.Change.UPDATED;
 +        return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, 
Event.SchemaChange.Target.TABLE, keyspace(), columnFamily());
      }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java
index 5acfdea,0000000..8bcaaf6
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java
@@@ -1,150 -1,0 +1,153 @@@
 +/*
 + * 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.auth.Permission;
 +import org.apache.cassandra.config.*;
 +import org.apache.cassandra.cql3.*;
 +import org.apache.cassandra.db.marshal.*;
 +import org.apache.cassandra.exceptions.*;
 +import org.apache.cassandra.service.ClientState;
 +import org.apache.cassandra.service.MigrationManager;
 +import org.apache.cassandra.transport.Event;
 +
 +public class DropTypeStatement extends SchemaAlteringStatement
 +{
 +    private final UTName name;
 +    private final boolean ifExists;
 +
 +    public DropTypeStatement(UTName name, boolean ifExists)
 +    {
 +        super();
 +        this.name = name;
 +        this.ifExists = ifExists;
 +    }
 +
 +    @Override
 +    public void prepareKeyspace(ClientState state) throws 
InvalidRequestException
 +    {
 +        if (!name.hasKeyspace())
 +            name.setKeyspace(state.getKeyspace());
 +    }
 +
 +    public void checkAccess(ClientState state) throws UnauthorizedException, 
InvalidRequestException
 +    {
 +        state.hasKeyspaceAccess(keyspace(), Permission.DROP);
 +    }
 +
 +    public void validate(ClientState state) throws RequestValidationException
 +    {
 +        KSMetaData ksm = Schema.instance.getKSMetaData(name.getKeyspace());
 +        if (ksm == null)
 +            throw new InvalidRequestException(String.format("Cannot drop type 
in unknown keyspace %s", name.getKeyspace()));
 +
 +        UserType old = ksm.userTypes.getType(name.getUserTypeName());
 +        if (old == null)
 +        {
 +            if (ifExists)
 +                return;
 +            else
 +                throw new InvalidRequestException(String.format("No user type 
named %s exists.", name));
 +        }
 +
 +        // We don't want to drop a type unless it's not used anymore (mainly 
because
 +        // if someone drops a type and recreates one with the same name but 
different
 +        // definition with the previous name still in use, things can get 
messy).
 +        // We have two places to check: 1) other user type that can nest the 
one
 +        // we drop and 2) existing tables referencing the type (maybe in a 
nested
 +        // way).
 +
 +        for (KSMetaData ksm2 : Schema.instance.getKeyspaceDefinitions())
 +        {
 +            for (UserType ut : ksm2.userTypes.getAllTypes().values())
 +            {
 +                if (ut.keyspace.equals(name.getKeyspace()) && 
ut.name.equals(name.getUserTypeName()))
 +                    continue;
 +                if (isUsedBy(ut))
 +                    throw new InvalidRequestException(String.format("Cannot 
drop user type %s as it is still used by user type %s", name, ut.asCQL3Type()));
 +            }
 +
 +            for (CFMetaData cfm : ksm2.cfMetaData().values())
 +                for (ColumnDefinition def : cfm.allColumns())
 +                    if (isUsedBy(def.type))
 +                        throw new 
InvalidRequestException(String.format("Cannot drop user type %s as it is still 
used by table %s.%s", name, cfm.ksName, cfm.cfName));
 +        }
 +    }
 +
 +    private boolean isUsedBy(AbstractType<?> toCheck) throws 
RequestValidationException
 +    {
 +        if (toCheck instanceof UserType)
 +        {
 +            UserType ut = (UserType)toCheck;
 +            if (name.getKeyspace().equals(ut.keyspace) && 
name.getUserTypeName().equals(ut.name))
 +                return true;
 +
 +            for (AbstractType<?> subtype : ut.fieldTypes())
 +                if (isUsedBy(subtype))
 +                    return true;
 +        }
 +        else if (toCheck instanceof CompositeType)
 +        {
 +            CompositeType ct = (CompositeType)toCheck;
 +            for (AbstractType<?> subtype : ct.types)
 +                if (isUsedBy(subtype))
 +                    return true;
 +        }
 +        else if (toCheck instanceof ColumnToCollectionType)
 +        {
 +            for (CollectionType collection : 
((ColumnToCollectionType)toCheck).defined.values())
 +                if (isUsedBy(collection))
 +                    return true;
 +        }
 +        else if (toCheck instanceof CollectionType)
 +        {
 +            if (toCheck instanceof ListType)
 +                return isUsedBy(((ListType)toCheck).elements);
 +            else if (toCheck instanceof SetType)
 +                return isUsedBy(((SetType)toCheck).elements);
 +            else
 +                return isUsedBy(((MapType)toCheck).keys) || 
isUsedBy(((MapType)toCheck).keys);
 +        }
 +        return false;
 +    }
 +
 +    public Event.SchemaChange changeEvent()
 +    {
 +        return new Event.SchemaChange(Event.SchemaChange.Change.DROPPED, 
Event.SchemaChange.Target.TYPE, keyspace(), name.getStringTypeName());
 +    }
 +
 +    @Override
 +    public String keyspace()
 +    {
 +        return name.getKeyspace();
 +    }
 +
-     public void announceMigration(boolean isLocalOnly) throws 
InvalidRequestException, ConfigurationException
++    public boolean announceMigration(boolean isLocalOnly) throws 
InvalidRequestException, ConfigurationException
 +    {
 +        KSMetaData ksm = Schema.instance.getKSMetaData(name.getKeyspace());
 +        assert ksm != null;
 +
 +        UserType toDrop = ksm.userTypes.getType(name.getUserTypeName());
 +        // Can be null with ifExists
-         if (toDrop != null)
-             MigrationManager.announceTypeDrop(toDrop, isLocalOnly);
++        if (toDrop == null)
++            return false;
++
++        MigrationManager.announceTypeDrop(toDrop, isLocalOnly);
++        return true;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/664efd41/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
index e70aac9,845d8cc..8882871
--- a/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
@@@ -63,26 -62,31 +63,34 @@@ public abstract class SchemaAlteringSta
          return new Prepared(this);
      }
  
 -    public abstract ResultMessage.SchemaChange.Change changeType();
 +    public abstract Event.SchemaChange changeEvent();
  
-     public abstract void announceMigration(boolean isLocalOnly) throws 
RequestValidationException;
+     /**
+      * Announces the migration to other nodes in the cluster.
+      * @return true if the execution of this statement resulted in a schema 
change, false otherwise (when IF NOT EXISTS
+      * is used, for example)
+      * @throws RequestValidationException
+      */
 -    public abstract boolean announceMigration() throws 
RequestValidationException;
++    public abstract boolean announceMigration(boolean isLocalOnly) throws 
RequestValidationException;
  
      public ResultMessage execute(QueryState state, QueryOptions options) 
throws RequestValidationException
      {
-         announceMigration(false);
-         return new ResultMessage.SchemaChange(changeEvent());
+         // If an IF [NOT] EXISTS clause was used, this may not result in an 
actual schema change.  To avoid doing
+         // extra work in the drivers to handle schema changes, we return an 
empty message in this case. (CASSANDRA-7600)
 -        boolean didChangeSchema = announceMigration();
 -        if (!didChangeSchema)
 -            return new ResultMessage.Void();
 -
 -        String tableName = cfName == null || columnFamily() == null ? "" : 
columnFamily();
 -        return new ResultMessage.SchemaChange(changeType(), keyspace(), 
tableName);
++        boolean didChangeSchema = announceMigration(false);
++        return didChangeSchema ? new 
ResultMessage.SchemaChange(changeEvent()) : new ResultMessage.Void();
      }
  
      public ResultMessage executeInternal(QueryState state, QueryOptions 
options)
      {
 -        // executeInternal is for local query only, thus altering schema is 
not supported
 -        throw new UnsupportedOperationException();
 +        try
 +        {
-             announceMigration(true);
-             return new ResultMessage.SchemaChange(changeEvent());
++            boolean didChangeSchema = announceMigration(true);
++            return didChangeSchema ? new 
ResultMessage.SchemaChange(changeEvent()) : new ResultMessage.Void();
 +        }
 +        catch (RequestValidationException e)
 +        {
 +            throw new RuntimeException(e);
 +        }
      }
  }

Reply via email to