cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From alek...@apache.org
Subject [05/11] cassandra git commit: 13426: work in progress
Date Wed, 31 May 2017 17:51:44 GMT
http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java
new file mode 100644
index 0000000..d5d2c97
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java
@@ -0,0 +1,417 @@
+/*
+ * 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.schema;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.apache.cassandra.auth.DataResource;
+import org.apache.cassandra.auth.IResource;
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.statements.TableAttributes;
+import org.apache.cassandra.db.CompactTables;
+import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.exceptions.AlreadyExistsException;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+import org.apache.cassandra.transport.Event.SchemaChange.Target;
+
+import static java.util.Comparator.comparing;
+
+import static com.google.common.collect.Iterables.concat;
+
+public final class CreateTableStatement extends AlterSchemaStatement
+{
+    private final String tableName;
+
+    private final Map<ColumnIdentifier, CQL3Type.Raw> rawColumns;
+    private final Set<ColumnIdentifier> staticColumns;
+    private final List<ColumnIdentifier> partitionKeyColumns;
+    private final List<ColumnIdentifier> clusteringColumns;
+
+    private final boolean useCompactStorage;
+    private final LinkedHashMap<ColumnIdentifier, Boolean> clusteringOrder;
+    private final TableAttributes attrs;
+
+    private final boolean ifNotExists;
+
+    public CreateTableStatement(String keyspaceName,
+                                String tableName,
+
+                                Map<ColumnIdentifier, CQL3Type.Raw> rawColumns,
+                                Set<ColumnIdentifier> staticColumns,
+                                List<ColumnIdentifier> partitionKeyColumns,
+                                List<ColumnIdentifier> clusteringColumns,
+
+                                boolean useCompactStorage,
+                                LinkedHashMap<ColumnIdentifier, Boolean> clusteringOrder,
+                                TableAttributes attrs,
+
+                                boolean ifNotExists)
+    {
+        super(keyspaceName);
+        this.tableName = tableName;
+
+        this.rawColumns = rawColumns;
+        this.staticColumns = staticColumns;
+        this.partitionKeyColumns = partitionKeyColumns;
+        this.clusteringColumns = clusteringColumns;
+
+        this.useCompactStorage = useCompactStorage;
+        this.clusteringOrder = clusteringOrder;
+        this.attrs = attrs;
+
+        this.ifNotExists = ifNotExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+        if (null == keyspace)
+            throw ire("Keyspace '%s' doesn't exist", keyspaceName);
+
+        if (keyspace.hasTable(tableName))
+        {
+            if (ifNotExists)
+                return schema;
+
+            throw new AlreadyExistsException(keyspaceName, tableName);
+        }
+
+        TableMetadata table = builder(keyspace.types).build();
+        table.validate();
+
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.tables.with(table)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.CREATED, Target.TABLE, keyspaceName, tableName);
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureKeyspacePermission(keyspaceName, Permission.CREATE);
+    }
+
+    @Override
+    Set<IResource> createdResources(KeyspacesDiff diff)
+    {
+        return ImmutableSet.of(DataResource.table(keyspaceName, tableName));
+    }
+
+    public TableMetadata.Builder builder(Types types)
+    {
+        attrs.validate();
+        TableParams params = attrs.asNewTableParams();
+
+        // use a TreeMap to preserve ordering across JDK versions (see CASSANDRA-9492) - important for stable unit tests
+        Map<ColumnIdentifier, CQL3Type> columns = new TreeMap<>(comparing(o -> o.bytes));
+        rawColumns.forEach((column, type) -> columns.put(column, type.prepare(keyspaceName, types)));
+
+        // check for nested non-frozen UDTs or collections in a non-frozen UDT
+        columns.forEach((column, type) ->
+        {
+            if (type.isUDT() && type.getType().isMultiCell())
+            {
+                ((UserType) type.getType()).fieldTypes().forEach(field ->
+                {
+                    if (field.isMultiCell())
+                        throw ire("Non-frozen UDTs with nested non-frozen collections are not supported");
+                });
+            }
+        });
+
+        /*
+         * Deal with PRIMARY KEY columns
+         */
+
+        HashSet<ColumnIdentifier> primaryKeyColumns = new HashSet<>();
+        concat(partitionKeyColumns, clusteringColumns).forEach(column ->
+        {
+            CQL3Type type = columns.get(column);
+            if (null == type)
+                throw ire("Unknown column '%s' referenced in PRIMARY KEY for table '%s'", column, tableName);
+
+            if (!primaryKeyColumns.add(column))
+                throw ire("Duplicate column '%s' in PRIMARY KEY clause for table '%s'", column, tableName);
+
+            if (type.getType().isMultiCell())
+            {
+                if (type.isCollection())
+                    throw ire("Invalid non-frozen collection type %s for PRIMARY KEY column '%s'", type, column);
+                else
+                    throw ire("Invalid non-frozen user-defined type %s for PRIMARY KEY column '%s'", type, column);
+            }
+
+            if (type.getType().isCounter())
+                throw ire("counter type is not supported for PRIMARY KEY column '%s'", column);
+
+            if (type.getType().referencesDuration())
+                throw ire("duration type is not supported for PRIMARY KEY column '%s'", column);
+
+            if (staticColumns.contains(column))
+                throw ire("Static column '%s' cannot be part of the PRIMARY KEY", column);
+        });
+
+        List<AbstractType<?>> partitionKeyTypes = new ArrayList<>();
+        List<AbstractType<?>> clusteringTypes = new ArrayList<>();
+
+        partitionKeyColumns.forEach(column ->
+        {
+            CQL3Type type = columns.remove(column);
+            partitionKeyTypes.add(type.getType());
+        });
+
+        clusteringColumns.forEach(column ->
+        {
+            CQL3Type type = columns.remove(column);
+            boolean reverse = !clusteringOrder.getOrDefault(column, true);
+            clusteringTypes.add(reverse ? ReversedType.getInstance(type.getType()) : type.getType());
+        });
+
+        // If we give a clustering order, we must explicitly do so for all aliases and in the order of the PK
+        // This wasn't previously enforced because of a bug in the implementation
+        if (!clusteringOrder.isEmpty() && !clusteringColumns.equals(new ArrayList<>(clusteringOrder.keySet())))
+            throw ire("Clustering key columns must exactly match columns in CLUSTERING ORDER BY directive");
+
+        // Static columns only make sense if we have at least one clustering column. Otherwise everything is static anyway
+        if (clusteringColumns.isEmpty() && !staticColumns.isEmpty())
+            throw ire("Static columns are only useful (and thus allowed) if the table has at least one clustering column");
+
+        /*
+         * Counter table validation
+         */
+
+        boolean hasCounters = rawColumns.values().stream().anyMatch(CQL3Type.Raw::isCounter);
+        if (hasCounters)
+        {
+            // We've handled anything that is not a PRIMARY KEY so columns only contains NON-PK columns. So
+            // if it's a counter table, make sure we don't have non-counter types
+            if (columns.values().stream().anyMatch(t -> !t.getType().isCounter()))
+                throw ire("Cannot mix counter and non counter columns in the same table");
+
+            if (params.defaultTimeToLive > 0)
+                throw ire("Cannot set %s on a table with counters", TableParams.Option.DEFAULT_TIME_TO_LIVE);
+        }
+
+        /*
+         * COMPACT STORAGE fun
+         */
+
+        // Dense meant, back with thrift, that no part of the "thrift column name" stores a "CQL/metadata column name".
+        // This means COMPACT STORAGE with at least one clustering type (otherwise it's a "static" CF).
+        boolean isDense = useCompactStorage && !clusteringColumns.isEmpty();
+
+        // Compound meant the "thrift column name" was a composite one. It's the case unless
+        // we use compact storage COMPACT STORAGE and we have either no clustering columns ("static" CF) or
+        // only one of them (if more than one, it's a "dense composite").
+        boolean isCompound = !(useCompactStorage && clusteringColumns.size() <= 1);
+
+        // For COMPACT STORAGE, we reject any "feature" that we wouldn't be able to translate back to thrift
+        if (useCompactStorage)
+        {
+            if (columns.values().stream().anyMatch(t -> t.getType().isMultiCell()))
+                throw ire("Non-frozen collections and UDTs are not supported with COMPACT STORAGE");
+
+            if (!staticColumns.isEmpty())
+                throw ire("Static columns are not supported in COMPACT STORAGE tables");
+
+            if (clusteringColumns.isEmpty())
+            {
+                // It's a thrift "static CF" so there should be some columns definition
+                if (columns.isEmpty())
+                    throw ire("No definition found that is not part of the PRIMARY KEY");
+            }
+
+            // We can have no columns (only the PK), but we can't have more than one.
+            if (isDense && columns.size() > 1)
+            {
+                throw ire("COMPACT STORAGE with composite PRIMARY KEY allows no more than one column not part of the PRIMARY KEY (got: %s)",
+                          columns.keySet().stream().map(ColumnIdentifier::toString).collect(Collectors.joining(", ")));
+            }
+
+            // we are in the "static" case, so we need at least one column defined. For non-compact however, having just the PK is fine.
+            if (!isDense && columns.isEmpty())
+                throw ire("COMPACT STORAGE with non-composite PRIMARY KEY require one column not part of the PRIMARY KEY, none given");
+        }
+
+        /*
+         * Create the builder
+         */
+
+        TableMetadata.Builder builder = TableMetadata.builder(keyspaceName, tableName);
+
+        if (attrs.hasProperty(TableAttributes.ID))
+            builder.id(attrs.getId());
+
+        builder.isDense(isDense)
+               .isCompound(isCompound)
+               .isCounter(hasCounters)
+               .params(params);
+
+        for (int i = 0; i < partitionKeyColumns.size(); i++)
+            builder.addPartitionKeyColumn(partitionKeyColumns.get(i), partitionKeyTypes.get(i));
+
+        for (int i = 0; i < clusteringColumns.size(); i++)
+            builder.addClusteringColumn(clusteringColumns.get(i), clusteringTypes.get(i));
+
+        boolean isStaticCompact = !isDense && !isCompound;
+        columns.forEach((column, type) ->
+        {
+            // Note that for "static" no-clustering compact storage we use static for the defined columns
+            if (staticColumns.contains(column) || isStaticCompact)
+                builder.addStaticColumn(column, type.getType());
+            else
+                builder.addRegularColumn(column, type.getType());
+
+        });
+
+        boolean isCompactTable = isDense || !isCompound;
+        if (isCompactTable)
+        {
+            CompactTables.DefaultNames names = CompactTables.defaultNameGenerator(builder.columnNames());
+            if (isStaticCompact)
+            {
+                // Compact tables always have a clustering and a single regular value.
+                builder.addClusteringColumn(names.defaultClusteringName(), UTF8Type.instance);
+                builder.addRegularColumn(names.defaultCompactValueName(), hasCounters ? CounterColumnType.instance : BytesType.instance);
+            }
+            else if (!builder.hasRegularColumns())
+            {
+                // Even for dense, we might not have our regular column if it wasn't part of the declaration. If
+                // that's the case, add it but with a specific EmptyType so we can recognize that case later
+                builder.addRegularColumn(names.defaultCompactValueName(), EmptyType.instance);
+            }
+        }
+
+        return builder;
+    }
+
+    public static TableMetadata.Builder parse(String cql, String keyspace)
+    {
+        return CQLFragmentParser.parseAny(CqlParser::createTableStatement, cql, "CREATE TABLE")
+                                .keyspace(keyspace)
+                                .prepare(null) // works around a messy ClientState/QueryProcessor class init deadlock
+                                .builder(Types.none());
+    }
+
+    public final static class Raw extends CQLStatement.Raw
+    {
+        private final QualifiedName name;
+        private final boolean ifNotExists;
+
+        private final Map<ColumnIdentifier, CQL3Type.Raw> rawColumns = new HashMap<>();
+        private final Set<ColumnIdentifier> staticColumns = new HashSet<>();
+        private final List<ColumnIdentifier> clusteringColumns = new ArrayList<>();
+
+        private List<ColumnIdentifier> partitionKeyColumns;
+
+        private boolean useCompactStorage;
+        private final LinkedHashMap<ColumnIdentifier, Boolean> clusteringOrder = new LinkedHashMap<>();
+        public final TableAttributes attrs = new TableAttributes();
+
+        public Raw(QualifiedName name, boolean ifNotExists)
+        {
+            this.name = name;
+            this.ifNotExists = ifNotExists;
+        }
+
+        public CreateTableStatement prepare(ClientState state)
+        {
+            String keyspaceName = name.hasKeyspace() ? name.getKeyspace() : state.getKeyspace();
+
+            if (null == partitionKeyColumns)
+                throw ire("No PRIMARY KEY specifed for table '%s' (exactly one required)", name);
+
+            return new CreateTableStatement(keyspaceName,
+                                            name.getName(),
+
+                                            rawColumns,
+                                            staticColumns,
+                                            partitionKeyColumns,
+                                            clusteringColumns,
+
+                                            useCompactStorage,
+                                            clusteringOrder,
+                                            attrs,
+
+                                            ifNotExists);
+        }
+
+        public String keyspace()
+        {
+            return name.getKeyspace();
+        }
+
+        public Raw keyspace(String keyspace)
+        {
+            name.setKeyspace(keyspace, true);
+            return this;
+        }
+
+        public String table()
+        {
+            return name.getName();
+        }
+
+        public void addColumn(ColumnIdentifier column, CQL3Type.Raw type, boolean isStatic)
+        {
+            if (null != rawColumns.put(column, type))
+                throw ire("Duplicate column '%s' declaration for table '%s'", column, name);
+
+            if (isStatic)
+                staticColumns.add(column);
+        }
+
+        public void setPartitionKeyColumn(ColumnIdentifier column)
+        {
+            setPartitionKeyColumns(Collections.singletonList(column));
+        }
+
+        public void setPartitionKeyColumns(List<ColumnIdentifier> columns)
+        {
+            if (null != partitionKeyColumns)
+                throw ire("Multiple PRIMARY KEY specified for table '%s' (exactly one required)", name);
+
+            partitionKeyColumns = columns;
+        }
+
+        public void markClusteringColumn(ColumnIdentifier column)
+        {
+            clusteringColumns.add(column);
+        }
+
+        public void useCompactStorage()
+        {
+            useCompactStorage = true;
+        }
+
+        public void extendClusteringOrder(ColumnIdentifier column, boolean ascending)
+        {
+            if (null != clusteringOrder.put(column, ascending))
+                throw ire("Duplicate column '%s' in CLUSTERING ORDER BY clause for table '%s'", column, name);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/CreateTriggerStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateTriggerStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTriggerStatement.java
new file mode 100644
index 0000000..e3f4b34
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTriggerStatement.java
@@ -0,0 +1,112 @@
+/*
+ * 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.schema;
+
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.QualifiedName;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.triggers.TriggerExecutor;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+import org.apache.cassandra.transport.Event.SchemaChange.Target;
+
+public final class CreateTriggerStatement extends AlterSchemaStatement
+{
+    private final String tableName;
+    private final String triggerName;
+    private final String triggerClass;
+    private final boolean ifNotExists;
+
+    public CreateTriggerStatement(String keyspaceName, String tableName, String triggerName, String triggerClass, boolean ifNotExists)
+    {
+        super(keyspaceName);
+        this.tableName = tableName;
+        this.triggerName = triggerName;
+        this.triggerClass = triggerClass;
+        this.ifNotExists = ifNotExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+        if (null == keyspace)
+            throw ire("Keyspace '%s' doesn't exist", keyspaceName);
+
+        TableMetadata table = keyspace.getTableOrViewNullable(tableName);
+        if (null == table)
+            throw ire("Table '%s' doesn't exist", tableName);
+
+        if (table.isView())
+            throw ire("Cannot CREATE TRIGGER for a materialized view");
+
+        TriggerMetadata existingTrigger = table.triggers.get(triggerName).orElse(null);
+        if (null != existingTrigger)
+        {
+            if (ifNotExists)
+                return schema;
+
+            throw ire("Trigger '%s' already exists", triggerName);
+        }
+
+        try
+        {
+            TriggerExecutor.instance.loadTriggerInstance(triggerClass);
+        }
+        catch (Exception e)
+        {
+            throw ire("Trigger class '%s' couldn't be loaded", triggerClass);
+        }
+
+        TableMetadata newTable = table.withSwapped(table.triggers.with(TriggerMetadata.create(triggerName, triggerClass)));
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.tables.withSwapped(newTable)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.UPDATED, Target.TABLE, keyspaceName, tableName);
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureIsSuperUser("Only superusers are allowed to perform CREATE TRIGGER queries");
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final QualifiedName tableName;
+        private final String triggerName;
+        private final String triggerClass;
+        private final boolean ifNotExists;
+
+        public Raw(QualifiedName tableName, String triggerName, String triggerClass, boolean ifNotExists)
+        {
+            this.tableName = tableName;
+            this.triggerName = triggerName;
+            this.triggerClass = triggerClass;
+            this.ifNotExists = ifNotExists;
+        }
+
+        public CreateTriggerStatement prepare(ClientState state)
+        {
+            String keyspaceName = tableName.hasKeyspace() ? tableName.getKeyspace() : state.getKeyspace();
+            return new CreateTriggerStatement(keyspaceName, tableName.getName(), triggerName, triggerClass, ifNotExists);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/CreateTypeStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTypeStatement.java
new file mode 100644
index 0000000..3d9ce07
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTypeStatement.java
@@ -0,0 +1,143 @@
+/*
+ * 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.schema;
+
+import java.util.*;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQL3Type;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.FieldIdentifier;
+import org.apache.cassandra.cql3.UTName;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.UserType;
+import org.apache.cassandra.schema.KeyspaceMetadata;
+import org.apache.cassandra.schema.Keyspaces;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.schema.Types;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+import org.apache.cassandra.transport.Event.SchemaChange.Target;
+
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+
+import static java.util.stream.Collectors.toList;
+
+public final class CreateTypeStatement extends AlterSchemaStatement
+{
+    private final String typeName;
+    private final List<FieldIdentifier> fieldNames;
+    private final List<CQL3Type.Raw> rawFieldTypes;
+    private final boolean ifNotExists;
+
+    public CreateTypeStatement(String keyspaceName,
+                               String typeName,
+                               List<FieldIdentifier> fieldNames,
+                               List<CQL3Type.Raw> rawFieldTypes,
+                               boolean ifNotExists)
+    {
+        super(keyspaceName);
+        this.typeName = typeName;
+        this.fieldNames = fieldNames;
+        this.rawFieldTypes = rawFieldTypes;
+        this.ifNotExists = ifNotExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+        if (null == keyspace)
+            throw ire("Keyspace '%s' doesn't exist", keyspaceName);
+
+        UserType existingType = keyspace.types.getNullable(bytes(typeName));
+        if (null != existingType)
+        {
+            if (ifNotExists)
+                return schema;
+
+            throw ire("A user type with name '%s' already exists", typeName);
+        }
+
+        Set<FieldIdentifier> usedNames = new HashSet<>();
+        for (FieldIdentifier name : fieldNames)
+            if (!usedNames.add(name))
+                throw ire("Duplicate field name '%s' in type '%s'", name, typeName);
+
+        for (CQL3Type.Raw type : rawFieldTypes)
+        {
+            if (type.isCounter())
+                throw ire("A user type cannot contain counters");
+
+            if (type.isUDT() && !type.isFrozen())
+                throw ire("A user type cannot contain non-frozen UDTs");
+        }
+
+        List<AbstractType<?>> fieldTypes =
+            rawFieldTypes.stream()
+                         .map(t -> t.prepare(keyspaceName, keyspace.types).getType())
+                         .collect(toList());
+
+        UserType udt = new UserType(keyspaceName, bytes(typeName), fieldNames, fieldTypes, true);
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.types.with(udt)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.CREATED, Target.TYPE, keyspaceName, typeName);
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureKeyspacePermission(keyspaceName, Permission.CREATE);
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final UTName name;
+        private final boolean ifNotExists;
+
+        private final List<FieldIdentifier> fieldNames = new ArrayList<>();
+        private final List<CQL3Type.Raw> rawFieldTypes = new ArrayList<>();
+
+        public Raw(UTName name, boolean ifNotExists)
+        {
+            this.name = name;
+            this.ifNotExists = ifNotExists;
+        }
+
+        public CreateTypeStatement prepare(ClientState state)
+        {
+            String keyspaceName = name.hasKeyspace() ? name.getKeyspace() : state.getKeyspace();
+            return new CreateTypeStatement(keyspaceName, name.getStringTypeName(), fieldNames, rawFieldTypes, ifNotExists);
+        }
+
+        public void addField(FieldIdentifier name, CQL3Type.Raw type)
+        {
+            fieldNames.add(name);
+            rawFieldTypes.add(type);
+        }
+
+        public void addToRawBuilder(Types.RawBuilder builder)
+        {
+            builder.add(name.getStringTypeName(),
+                        fieldNames.stream().map(FieldIdentifier::toString).collect(toList()),
+                        rawFieldTypes.stream().map(CQL3Type.Raw::toString).collect(toList()));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java
new file mode 100644
index 0000000..a90e145
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java
@@ -0,0 +1,400 @@
+/*
+ * 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.schema;
+
+import java.util.*;
+
+import com.google.common.collect.Lists;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
+import org.apache.cassandra.cql3.selection.RawSelector;
+import org.apache.cassandra.cql3.selection.Selectable;
+import org.apache.cassandra.cql3.statements.StatementType;
+import org.apache.cassandra.cql3.statements.TableAttributes;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.ReversedType;
+import org.apache.cassandra.exceptions.AlreadyExistsException;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+import org.apache.cassandra.transport.Event.SchemaChange.Target;
+
+import static java.lang.String.join;
+
+import static com.google.common.collect.Iterables.concat;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.transform;
+
+public final class CreateViewStatement extends AlterSchemaStatement
+{
+    private final String tableName;
+    private final String viewName;
+
+    private final List<RawSelector> rawColumns;
+    private final List<ColumnIdentifier> partitionKeyColumns;
+    private final List<ColumnIdentifier> clusteringColumns;
+
+    private final WhereClause whereClause;
+
+    private final boolean useCompactStorage;
+    private final LinkedHashMap<ColumnIdentifier, Boolean> clusteringOrder;
+    private final TableAttributes attrs;
+
+    private final boolean ifNotExists;
+
+    public CreateViewStatement(String keyspaceName,
+                               String tableName,
+                               String viewName,
+
+                               List<RawSelector> rawColumns,
+                               List<ColumnIdentifier> partitionKeyColumns,
+                               List<ColumnIdentifier> clusteringColumns,
+
+                               WhereClause whereClause,
+
+                               boolean useCompactStorage,
+                               LinkedHashMap<ColumnIdentifier, Boolean> clusteringOrder,
+                               TableAttributes attrs,
+
+                               boolean ifNotExists)
+    {
+        super(keyspaceName);
+        this.tableName = tableName;
+        this.viewName = viewName;
+
+        this.rawColumns = rawColumns;
+        this.partitionKeyColumns = partitionKeyColumns;
+        this.clusteringColumns = clusteringColumns;
+
+        this.whereClause = whereClause;
+
+        this.useCompactStorage = useCompactStorage;
+        this.clusteringOrder = clusteringOrder;
+        this.attrs = attrs;
+
+        this.ifNotExists = ifNotExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        /*
+         * Basic dependency validations
+         */
+
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+        if (null == keyspace)
+            throw ire("Keyspace '%s' doesn't exist", keyspaceName);
+
+        TableMetadata table = keyspace.tables.getNullable(tableName);
+        if (null == table)
+            throw ire("Base table '%s' doesn't exist", tableName);
+
+        if (keyspace.hasTable(viewName))
+            throw ire("Cannot create materialized view '%s' - a table with the same name already exists", viewName);
+
+        if (keyspace.hasView(viewName))
+        {
+            if (ifNotExists)
+                return schema;
+
+            throw new AlreadyExistsException(keyspaceName, viewName);
+        }
+
+        /*
+         * Base table validation
+         */
+
+        if (table.isCounter())
+            throw ire("Materialized views are not supported on counter tables");
+
+        if (table.isView())
+            throw ire("Materialized views cannot be created against other materialized views");
+
+        if (table.params.gcGraceSeconds == 0)
+        {
+            throw ire("Cannot create materialized view '%s' for base table " +
+                      "'%s' with gc_grace_seconds of 0, since this value is " +
+                      "used to TTL undelivered updates. Setting gc_grace_seconds" +
+                      " too low might cause undelivered updates to expire " +
+                      "before being replayed.",
+                      viewName, tableName);
+        }
+
+        /*
+         * Process SELECT clause
+         */
+
+        Set<ColumnIdentifier> selectedColumns = new HashSet<>();
+
+        if (rawColumns.isEmpty()) // SELECT *
+            table.columns().forEach(c -> selectedColumns.add(c.name));
+
+        rawColumns.forEach(selector ->
+        {
+            if (null != selector.alias)
+                throw ire("Cannot use aliases when defining a materialized view (got %s)", selector);
+
+            if (!(selector.selectable instanceof Selectable.RawIdentifier))
+                throw ire("Can only select columns by name when defining a materialized view (got %s)", selector.selectable);
+
+            // will throw IRE if the column doesn't exist in the base table
+            ColumnMetadata column = (ColumnMetadata) selector.selectable.prepare(table);
+
+            selectedColumns.add(column.name);
+        });
+
+        selectedColumns.stream()
+                       .map(table::getColumn)
+                       .filter(ColumnMetadata::isStatic)
+                       .findAny()
+                       .ifPresent(c -> { throw ire("Cannot include static column '%s' in materialized view '%s'", c, viewName); });
+
+        /*
+         * Process PRIMARY KEY columns and CLUSTERING ORDER BY clause
+         */
+
+        if (partitionKeyColumns.isEmpty())
+            throw ire("Must provide at least one partition key column for materialized view '%s'", viewName);
+
+        HashSet<ColumnIdentifier> primaryKeyColumns = new HashSet<>();
+
+        concat(partitionKeyColumns, clusteringColumns).forEach(name ->
+        {
+            ColumnMetadata column = table.getColumn(name);
+            if (null == column || !selectedColumns.contains(name))
+                throw ire("Unknown column '%s' referenced in PRIMARY KEY for materialized view '%s'", name, viewName);
+
+            if (!primaryKeyColumns.add(name))
+                throw ire("Duplicate column '%s' in PRIMARY KEY clause for materialized view '%s'", name, viewName);
+
+            AbstractType<?> type = column.type;
+
+            if (type.isMultiCell())
+            {
+                if (type.isCollection())
+                    throw ire("Invalid non-frozen collection type '%s' for PRIMARY KEY column '%s'", type, name);
+                else
+                    throw ire("Invalid non-frozen user-defined type '%s' for PRIMARY KEY column '%s'", type, name);
+            }
+
+            if (type.isCounter())
+                throw ire("counter type is not supported for PRIMARY KEY column '%s'", name);
+
+            if (type.referencesDuration())
+                throw ire("duration type is not supported for PRIMARY KEY column '%s'", name);
+        });
+
+        // If we give a clustering order, we must explicitly do so for all aliases and in the order of the PK
+        if (!clusteringOrder.isEmpty() && !clusteringColumns.equals(new ArrayList<>(clusteringOrder.keySet())))
+            throw ire("Clustering key columns must exactly match columns in CLUSTERING ORDER BY directive");
+
+        /*
+         * We need to include all of the primary key columns from the base table in order to make sure that we do not
+         * overwrite values in the view. We cannot support "collapsing" the base table into a smaller number of rows in
+         * the view because if we need to generate a tombstone, we have no way of knowing which value is currently being
+         * used in the view and whether or not to generate a tombstone. In order to not surprise our users, we require
+         * that they include all of the columns. We provide them with a list of all of the columns left to include.
+         */
+        List<ColumnIdentifier> missingPrimaryKeyColumns =
+            Lists.newArrayList(filter(transform(table.primaryKeyColumns(), c -> c.name), c -> !primaryKeyColumns.contains(c)));
+
+        if (!missingPrimaryKeyColumns.isEmpty())
+        {
+            throw ire("Cannot create materialized view '%s' without primary key columns %s from base table '%s'",
+                      viewName, join(", ", transform(missingPrimaryKeyColumns, ColumnIdentifier::toString)), tableName);
+        }
+
+        Set<ColumnIdentifier> regularBaseTableColumnsInViewPrimaryKey = new HashSet<>(primaryKeyColumns);
+        transform(table.primaryKeyColumns(), c -> c.name).forEach(regularBaseTableColumnsInViewPrimaryKey::remove);
+        if (regularBaseTableColumnsInViewPrimaryKey.size() > 1)
+        {
+            throw ire("Cannot include more than one non-primary key column in materialized view primary key (got %s)",
+                      join(", ", transform(regularBaseTableColumnsInViewPrimaryKey, ColumnIdentifier::toString)));
+        }
+
+        /*
+         * Process WHERE clause
+         */
+
+        if (whereClause.containsCustomExpressions())
+            throw ire("WHERE clause for materialized view '%s' cannot contain custom index expressions", viewName);
+
+        StatementRestrictions restrictions =
+            new StatementRestrictions(StatementType.SELECT,
+                                      table,
+                                      whereClause,
+                                      VariableSpecifications.empty(),
+                                      false,
+                                      false,
+                                      true,
+                                      true);
+
+        List<ColumnIdentifier> nonRestrictedPrimaryKeyColumns =
+            Lists.newArrayList(filter(primaryKeyColumns, name -> !restrictions.isRestricted(table.getColumn(name))));
+
+        if (!nonRestrictedPrimaryKeyColumns.isEmpty())
+        {
+            throw ire("Primary key columns %s must be restricted with 'IS NOT NULL' or otherwise",
+                      join(", ", transform(nonRestrictedPrimaryKeyColumns, ColumnIdentifier::toString)));
+        }
+
+        /*
+         * Validate WITH params
+         */
+
+        attrs.validate();
+
+        if (useCompactStorage)
+            throw ire("Cannot use 'COMPACT STORAGE' when defining a materialized view");
+
+        if (attrs.hasOption(TableParams.Option.DEFAULT_TIME_TO_LIVE))
+        {
+            throw ire("Cannot set default_time_to_live for a materialized view. " +
+                      "Data in a materialized view always expire at the same time than " +
+                      "the corresponding data in the parent table.");
+        }
+
+        /*
+         * Build the thing
+         */
+
+        TableMetadata.Builder builder = TableMetadata.builder(keyspaceName, viewName);
+
+        if (attrs.hasProperty(TableAttributes.ID))
+            builder.id(attrs.getId());
+
+        builder.params(attrs.asNewTableParams())
+               .isView(true);
+
+        partitionKeyColumns.forEach(name -> builder.addPartitionKeyColumn(name, getType(table, name)));
+        clusteringColumns.forEach(name -> builder.addClusteringColumn(name, getType(table, name)));
+
+        selectedColumns.stream()
+                       .filter(name -> !primaryKeyColumns.contains(name))
+                       .forEach(name -> builder.addRegularColumn(name, getType(table, name)));
+
+        ViewMetadata view = new ViewMetadata(table.id, table.name, rawColumns.isEmpty(), whereClause, builder.build());
+        view.metadata.validate();
+
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.views.with(view)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.CREATED, Target.TABLE, keyspaceName, viewName);
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureTablePermission(keyspaceName, tableName, Permission.ALTER);
+    }
+
+    private AbstractType<?> getType(TableMetadata table, ColumnIdentifier name)
+    {
+        AbstractType<?> type = table.getColumn(name).type;
+        boolean reverse = !clusteringOrder.getOrDefault(name, true);
+
+        if (type.isReversed() && !reverse)
+            return ((ReversedType) type).baseType;
+        else if (!type.isReversed() && reverse)
+            return ReversedType.getInstance(type);
+        else
+            return type;
+    }
+
+    public final static class Raw extends CQLStatement.Raw
+    {
+        private final QualifiedName tableName;
+        private final QualifiedName viewName;
+        private final boolean ifNotExists;
+
+        private final List<RawSelector> rawColumns;
+        private final List<ColumnIdentifier> clusteringColumns = new ArrayList<>();
+        private List<ColumnIdentifier> partitionKeyColumns;
+
+        private final WhereClause whereClause;
+
+        private boolean useCompactStorage;
+        private final LinkedHashMap<ColumnIdentifier, Boolean> clusteringOrder = new LinkedHashMap<>();
+        public final TableAttributes attrs = new TableAttributes();
+
+        public Raw(QualifiedName tableName, QualifiedName viewName, List<RawSelector> rawColumns, WhereClause whereClause, boolean ifNotExists)
+        {
+            this.tableName = tableName;
+            this.viewName = viewName;
+            this.rawColumns = rawColumns;
+            this.whereClause = whereClause;
+            this.ifNotExists = ifNotExists;
+        }
+
+        public CreateViewStatement prepare(ClientState state)
+        {
+            String keyspaceName = viewName.hasKeyspace() ? viewName.getKeyspace() : state.getKeyspace();
+
+            if (tableName.hasKeyspace() && !keyspaceName.equals(tableName.getKeyspace()))
+                throw ire("Cannot create a materialized view on a table in a different keyspace");
+
+            if (!bindVariables.isEmpty())
+                throw ire("Bind variables are not allowed in CREATE MATERIALIZED VIEW statements");
+
+            if (null == partitionKeyColumns)
+                throw ire("No PRIMARY KEY specifed for view '%s' (exactly one required)", viewName);
+
+            return new CreateViewStatement(keyspaceName,
+                                           tableName.getName(),
+                                           viewName.getName(),
+
+                                           rawColumns,
+                                           partitionKeyColumns,
+                                           clusteringColumns,
+
+                                           whereClause,
+
+                                           useCompactStorage,
+                                           clusteringOrder,
+                                           attrs,
+
+                                           ifNotExists);
+        }
+
+        public void setPartitionKeyColumns(List<ColumnIdentifier> columns)
+        {
+            partitionKeyColumns = columns;
+        }
+
+        public void markClusteringColumn(ColumnIdentifier column)
+        {
+            clusteringColumns.add(column);
+        }
+
+        public void useCompactStorage()
+        {
+            useCompactStorage = true;
+        }
+
+        public void extendClusteringOrder(ColumnIdentifier column, boolean ascending)
+        {
+            if (null != clusteringOrder.put(column, ascending))
+                throw ire("Duplicate column '%s' in CLUSTERING ORDER BY clause for view '%s'", column, viewName);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/DropAggregateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropAggregateStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropAggregateStatement.java
new file mode 100644
index 0000000..b024ca0
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropAggregateStatement.java
@@ -0,0 +1,166 @@
+/*
+ * 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.schema;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import org.apache.cassandra.auth.FunctionResource;
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQL3Type;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.functions.FunctionName;
+import org.apache.cassandra.cql3.functions.UDAggregate;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+
+import static java.lang.String.format;
+import static java.lang.String.join;
+import static java.util.stream.Collectors.toList;
+
+import static com.google.common.collect.Iterables.transform;
+
+public final class DropAggregateStatement extends AlterSchemaStatement
+{
+    private final String aggregateName;
+    private final List<CQL3Type.Raw> arguments;
+    private final boolean argumentsSpeficied;
+    private final boolean ifExists;
+
+    public DropAggregateStatement(String keyspaceName,
+                                  String aggregateName,
+                                  List<CQL3Type.Raw> arguments,
+                                  boolean argumentsSpeficied,
+                                  boolean ifExists)
+    {
+        super(keyspaceName);
+        this.aggregateName = aggregateName;
+        this.arguments = arguments;
+        this.argumentsSpeficied = argumentsSpeficied;
+        this.ifExists = ifExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        String name =
+            argumentsSpeficied
+          ? format("%s.%s(%s)", keyspaceName, aggregateName, join(", ", transform(arguments, CQL3Type.Raw::toString)))
+          : format("%s.%s", keyspaceName, aggregateName);
+
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+        if (null == keyspace)
+        {
+            if (ifExists)
+                return schema;
+
+            throw ire("Aggregate '%s' doesn't exist", name);
+        }
+
+        Collection<Function> aggregates = keyspace.functions.get(new FunctionName(keyspaceName, aggregateName));
+        if (aggregates.size() > 1 && !argumentsSpeficied)
+        {
+            throw ire("'DROP AGGREGATE %s' matches multiple function definitions; " +
+                      "specify the argument types by issuing a statement like " +
+                      "'DROP AGGREGATE %s (type, type, ...)'. You can use cqlsh " +
+                      "'DESCRIBE AGGREGATE %s' command to find all overloads",
+                      aggregateName, aggregateName, aggregateName);
+        }
+
+        arguments.stream()
+                 .filter(CQL3Type.Raw::isFrozen)
+                 .findFirst()
+                 .ifPresent(t -> { throw ire("Argument '%s' cannot be frozen; remove frozen<> modifier from '%s'", t, t); });
+
+        List<AbstractType<?>> argumentTypes = prepareArgumentTypes(keyspace.types);
+
+        Predicate<Function> filter = Functions.Filter.UDA;
+        if (argumentsSpeficied)
+            filter = filter.and(f -> Functions.typesMatch(f.argTypes(), argumentTypes));
+
+        Function aggregate = aggregates.stream().filter(filter).findAny().orElse(null);
+        if (null == aggregate)
+        {
+            if (ifExists)
+                return schema;
+
+            throw ire("Aggregate '%s' doesn't exist", name);
+        }
+
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.functions.without(aggregate)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        Functions dropped = diff.altered.get(0).udas.dropped;
+        assert dropped.size() == 1;
+        return SchemaChange.forAggregate(Change.DROPPED, (UDAggregate) dropped.iterator().next());
+    }
+
+    public void authorize(ClientState client)
+    {
+        KeyspaceMetadata keyspace = Schema.instance.getKeyspaceMetadata(keyspaceName);
+        if (null == keyspace)
+            return;
+
+        Stream<Function> functions = keyspace.functions.get(new FunctionName(keyspaceName, aggregateName)).stream();
+        if (argumentsSpeficied)
+            functions = functions.filter(f -> Functions.typesMatch(f.argTypes(), prepareArgumentTypes(keyspace.types)));
+
+        functions.forEach(f -> client.ensurePermission(Permission.DROP, FunctionResource.function(f)));
+    }
+
+    private List<AbstractType<?>> prepareArgumentTypes(Types types)
+    {
+        return arguments.stream()
+                        .map(t -> t.prepare(keyspaceName, types))
+                        .map(CQL3Type::getType)
+                        .collect(toList());
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final FunctionName name;
+        private final List<CQL3Type.Raw> arguments;
+        private final boolean argumentsSpecified;
+        private final boolean ifExists;
+
+        public Raw(FunctionName name,
+                   List<CQL3Type.Raw> arguments,
+                   boolean argumentsSpecified,
+                   boolean ifExists)
+        {
+            this.name = name;
+            this.arguments = arguments;
+            this.argumentsSpecified = argumentsSpecified;
+            this.ifExists = ifExists;
+        }
+
+        public DropAggregateStatement prepare(ClientState state)
+        {
+            String keyspaceName = name.hasKeyspace() ? name.keyspace : state.getKeyspace();
+            return new DropAggregateStatement(keyspaceName, name.name, arguments, argumentsSpecified, ifExists);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/DropFunctionStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropFunctionStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropFunctionStatement.java
new file mode 100644
index 0000000..8b3341c
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropFunctionStatement.java
@@ -0,0 +1,168 @@
+/*
+ * 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.schema;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import org.apache.cassandra.auth.FunctionResource;
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQL3Type;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.functions.*;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+
+import static java.lang.String.format;
+import static java.lang.String.join;
+import static java.util.stream.Collectors.toList;
+
+import static com.google.common.collect.Iterables.transform;
+
+public final class DropFunctionStatement extends AlterSchemaStatement
+{
+    private final String functionName;
+    private final Collection<CQL3Type.Raw> arguments;
+    private final boolean argumentsSpeficied;
+    private final boolean ifExists;
+
+    public DropFunctionStatement(String keyspaceName,
+                                 String functionName,
+                                 Collection<CQL3Type.Raw> arguments,
+                                 boolean argumentsSpeficied,
+                                 boolean ifExists)
+    {
+        super(keyspaceName);
+        this.functionName = functionName;
+        this.arguments = arguments;
+        this.argumentsSpeficied = argumentsSpeficied;
+        this.ifExists = ifExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        String name =
+            argumentsSpeficied
+          ? format("%s.%s(%s)", keyspaceName, functionName, join(", ", transform(arguments, CQL3Type.Raw::toString)))
+          : format("%s.%s", keyspaceName, functionName);
+
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+        if (null == keyspace)
+        {
+            if (ifExists)
+                return schema;
+
+            throw ire("Function '%s' doesn't exist", name);
+        }
+
+        Collection<Function> functions = keyspace.functions.get(new FunctionName(keyspaceName, functionName));
+        if (functions.size() > 1 && !argumentsSpeficied)
+        {
+            throw ire("'DROP FUNCTION %s' matches multiple function definitions; " +
+                      "specify the argument types by issuing a statement like " +
+                      "'DROP FUNCTION %s (type, type, ...)'. You can use cqlsh " +
+                      "'DESCRIBE FUNCTION %s' command to find all overloads",
+                      functionName, functionName, functionName);
+        }
+
+        arguments.stream()
+                 .filter(CQL3Type.Raw::isFrozen)
+                 .findFirst()
+                 .ifPresent(t -> { throw ire("Argument '%s' cannot be frozen; remove frozen<> modifier from '%s'", t, t); });
+
+        List<AbstractType<?>> argumentTypes = prepareArgumentTypes(keyspace.types);
+
+        Predicate<Function> filter = Functions.Filter.UDF;
+        if (argumentsSpeficied)
+            filter = filter.and(f -> Functions.typesMatch(f.argTypes(), argumentTypes));
+
+        Function function = functions.stream().filter(filter).findAny().orElse(null);
+        if (null == function)
+        {
+            if (ifExists)
+                return schema;
+
+            throw ire("Function '%s' doesn't exist", name);
+        }
+
+        Collection<UDAggregate> dependentAggregates = keyspace.functions.aggregatesUsingFunction(function);
+        if (!dependentAggregates.isEmpty())
+            throw ire("Function '%s' is still referenced by %s", name, dependentAggregates);
+
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.functions.without(function)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        Functions dropped = diff.altered.get(0).udfs.dropped;
+        assert dropped.size() == 1;
+        return SchemaChange.forFunction(Change.DROPPED, (UDFunction) dropped.iterator().next());
+    }
+
+    public void authorize(ClientState client)
+    {
+        KeyspaceMetadata keyspace = Schema.instance.getKeyspaceMetadata(keyspaceName);
+        if (null == keyspace)
+            return;
+
+        Stream<Function> functions = keyspace.functions.get(new FunctionName(keyspaceName, functionName)).stream();
+        if (argumentsSpeficied)
+            functions = functions.filter(f -> Functions.typesMatch(f.argTypes(), prepareArgumentTypes(keyspace.types)));
+
+        functions.forEach(f -> client.ensurePermission(Permission.DROP, FunctionResource.function(f)));
+    }
+
+    private List<AbstractType<?>> prepareArgumentTypes(Types types)
+    {
+        return arguments.stream()
+                        .map(t -> t.prepare(keyspaceName, types))
+                        .map(CQL3Type::getType)
+                        .collect(toList());
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final FunctionName name;
+        private final List<CQL3Type.Raw> arguments;
+        private final boolean argumentsSpecified;
+        private final boolean ifExists;
+
+        public Raw(FunctionName name,
+                   List<CQL3Type.Raw> arguments,
+                   boolean argumentsSpecified,
+                   boolean ifExists)
+        {
+            this.name = name;
+            this.arguments = arguments;
+            this.argumentsSpecified = argumentsSpecified;
+            this.ifExists = ifExists;
+        }
+
+        public DropFunctionStatement prepare(ClientState state)
+        {
+            String keyspaceName = name.hasKeyspace() ? name.keyspace : state.getKeyspace();
+            return new DropFunctionStatement(keyspaceName, name.name, arguments, argumentsSpecified, ifExists);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/DropIndexStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropIndexStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropIndexStatement.java
new file mode 100644
index 0000000..32a30eb
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropIndexStatement.java
@@ -0,0 +1,102 @@
+/*
+ * 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.schema;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.QualifiedName;
+import org.apache.cassandra.schema.Diff;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.schema.KeyspaceMetadata.KeyspaceDiff;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+import org.apache.cassandra.transport.Event.SchemaChange.Target;
+
+public final class DropIndexStatement extends AlterSchemaStatement
+{
+    private final String indexName;
+    private final boolean ifExists;
+
+    public DropIndexStatement(String keyspaceName, String indexName, boolean ifExists)
+    {
+        super(keyspaceName);
+        this.indexName = indexName;
+        this.ifExists = ifExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+
+        TableMetadata table = null == keyspace
+                            ? null
+                            : keyspace.findIndexedTable(indexName).orElse(null);
+
+        if (null == table)
+        {
+            if (ifExists)
+                return schema;
+
+            throw ire("Index '%s.%s' doesn't exist'", keyspaceName, indexName);
+        }
+
+        TableMetadata newTable = table.withSwapped(table.indexes.without(indexName));
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.tables.withSwapped(newTable)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        assert diff.altered.size() == 1;
+        KeyspaceDiff ksDiff = diff.altered.get(0);
+
+        assert ksDiff.tables.altered.size() == 1;
+        Diff.Altered<TableMetadata> tableDiff = ksDiff.tables.altered.iterator().next();
+
+        return new SchemaChange(Change.UPDATED, Target.TABLE, keyspaceName, tableDiff.after.name);
+    }
+
+    public void authorize(ClientState client)
+    {
+        KeyspaceMetadata keyspace = Schema.instance.getKeyspaceMetadata(keyspaceName);
+        if (null == keyspace)
+            return;
+
+        keyspace.findIndexedTable(indexName)
+                .ifPresent(t -> client.ensureTablePermission(keyspaceName, t.name, Permission.ALTER));
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final QualifiedName name;
+        private final boolean ifExists;
+
+        public Raw(QualifiedName name, boolean ifExists)
+        {
+            this.name = name;
+            this.ifExists = ifExists;
+        }
+
+        public DropIndexStatement prepare(ClientState state)
+        {
+            String keyspaceName = name.hasKeyspace() ? name.getKeyspace() : state.getKeyspace();
+            return new DropIndexStatement(keyspaceName, name.getName(), ifExists);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/DropKeyspaceStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropKeyspaceStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropKeyspaceStatement.java
new file mode 100644
index 0000000..358720d
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropKeyspaceStatement.java
@@ -0,0 +1,75 @@
+/*
+ * 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.schema;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.schema.Keyspaces;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+
+public final class DropKeyspaceStatement extends AlterSchemaStatement
+{
+    private final boolean ifExists;
+
+    public DropKeyspaceStatement(String keyspaceName, boolean ifExists)
+    {
+        super(keyspaceName);
+        this.ifExists = ifExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        if (schema.containsKeyspace(keyspaceName))
+            return schema.without(keyspaceName);
+
+        if (ifExists)
+            return schema;
+
+        throw ire("Keyspace '%s' doesn't exist", keyspaceName);
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.DROPPED, keyspaceName);
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureKeyspacePermission(keyspaceName, Permission.DROP);
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final String keyspaceName;
+        private final boolean ifExists;
+
+        public Raw(String keyspaceName, boolean ifExists)
+        {
+            this.keyspaceName = keyspaceName;
+            this.ifExists = ifExists;
+        }
+
+        public DropKeyspaceStatement prepare(ClientState state)
+        {
+            return new DropKeyspaceStatement(keyspaceName, ifExists);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/DropTableStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropTableStatement.java
new file mode 100644
index 0000000..7fdfbd8
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropTableStatement.java
@@ -0,0 +1,104 @@
+/*
+ * 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.schema;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.QualifiedName;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+import org.apache.cassandra.transport.Event.SchemaChange.Target;
+
+import static java.lang.String.join;
+
+import static com.google.common.collect.Iterables.isEmpty;
+import static com.google.common.collect.Iterables.transform;
+
+public final class DropTableStatement extends AlterSchemaStatement
+{
+    private final String tableName;
+    private final boolean ifExists;
+
+    public DropTableStatement(String keyspaceName, String tableName, boolean ifExists)
+    {
+        super(keyspaceName);
+        this.tableName = tableName;
+        this.ifExists = ifExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+
+        TableMetadata table = null == keyspace
+                            ? null
+                            : keyspace.getTableOrViewNullable(tableName);
+
+        if (null == table)
+        {
+            if (ifExists)
+                return schema;
+
+            throw ire("Table '%s.%s' doesn't exist", keyspaceName, tableName);
+        }
+
+        if (table.isView())
+            throw ire("Cannot use DROP TABLE on a materialized view. Please use DROP MATERIALIZED VIEW instead.");
+
+        Iterable<ViewMetadata> views = keyspace.views.forTable(table.id);
+        if (!isEmpty(views))
+        {
+            throw ire("Cannot drop a table when materialized views still depend on it (%s)",
+                      keyspaceName,
+                      join(", ", transform(views, ViewMetadata::name)));
+        }
+
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.tables.without(table)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.DROPPED, Target.TABLE, keyspaceName, tableName);
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureTablePermission(keyspaceName, tableName, Permission.DROP);
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final QualifiedName name;
+        private final boolean ifExists;
+
+        public Raw(QualifiedName name, boolean ifExists)
+        {
+            this.name = name;
+            this.ifExists = ifExists;
+        }
+
+        public DropTableStatement prepare(ClientState state)
+        {
+            String keyspaceName = name.hasKeyspace() ? name.getKeyspace() : state.getKeyspace();
+            return new DropTableStatement(keyspaceName, name.getName(), ifExists);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/DropTriggerStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropTriggerStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropTriggerStatement.java
new file mode 100644
index 0000000..ea5059a
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropTriggerStatement.java
@@ -0,0 +1,96 @@
+/*
+ * 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.schema;
+
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.QualifiedName;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+import org.apache.cassandra.transport.Event.SchemaChange.Target;
+
+public final class DropTriggerStatement extends AlterSchemaStatement
+{
+    private final String tableName;
+    private final String triggerName;
+    private final boolean ifExists;
+
+    public DropTriggerStatement(String keyspaceName, String tableName, String triggerName, boolean ifExists)
+    {
+        super(keyspaceName);
+        this.tableName = tableName;
+        this.triggerName = triggerName;
+        this.ifExists = ifExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+
+        TableMetadata table = null == keyspace
+                            ? null
+                            : keyspace.tables.getNullable(tableName);
+
+        TriggerMetadata trigger = null == table
+                                ? null
+                                : table.triggers.get(triggerName).orElse(null);
+
+        if (null == trigger)
+        {
+            if (ifExists)
+                return schema;
+
+            throw ire("Trigger '%s' on '%s.%s' doesn't exist", triggerName, keyspaceName, tableName);
+        }
+
+        TableMetadata newTable = table.withSwapped(table.triggers.without(triggerName));
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.tables.withSwapped(newTable)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.UPDATED, Target.TABLE, keyspaceName, tableName);
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureIsSuperUser("Only superusers are allowed to perfrom DROP TRIGGER queries");
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final QualifiedName tableName;
+        private final String triggerName;
+        private final boolean ifExists;
+
+        public Raw(QualifiedName tableName, String triggerName, boolean ifExists)
+        {
+            this.tableName = tableName;
+            this.triggerName = triggerName;
+            this.ifExists = ifExists;
+        }
+
+        public DropTriggerStatement prepare(ClientState state)
+        {
+            String keyspaceName = tableName.hasKeyspace() ? tableName.getKeyspace() : state.getKeyspace();
+            return new DropTriggerStatement(keyspaceName, tableName.getName(), triggerName, ifExists);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/DropTypeStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropTypeStatement.java
new file mode 100644
index 0000000..e58de93
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropTypeStatement.java
@@ -0,0 +1,140 @@
+/*
+ * 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.schema;
+
+import java.nio.ByteBuffer;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.UTName;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.db.marshal.UserType;
+import org.apache.cassandra.schema.KeyspaceMetadata;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.schema.Keyspaces;
+import org.apache.cassandra.schema.TableMetadata;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+import org.apache.cassandra.transport.Event.SchemaChange.Target;
+import org.apache.cassandra.transport.Event.SchemaChange;
+
+import static java.lang.String.join;
+
+import static com.google.common.collect.Iterables.isEmpty;
+import static com.google.common.collect.Iterables.transform;
+
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+
+public final class DropTypeStatement extends AlterSchemaStatement
+{
+    private final String typeName;
+    private final boolean ifExists;
+
+    public DropTypeStatement(String keyspaceName, String typeName, boolean ifExists)
+    {
+        super(keyspaceName);
+        this.typeName = typeName;
+        this.ifExists = ifExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        ByteBuffer name = bytes(typeName);
+
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+
+        UserType type = null == keyspace
+                      ? null
+                      : keyspace.types.getNullable(name);
+
+        if (null == type)
+        {
+            if (ifExists)
+                return schema;
+
+            throw ire("Type '%s.%s' doesn't exist", keyspaceName, typeName);
+        }
+
+        /*
+         * 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 three places to check:
+         * 1) UDFs and UDAs using the type
+         * 2) other user type that can nest the one we drop and
+         * 3) existing tables referencing the type (maybe in a nested way).
+         */
+        Iterable<Function> functions = keyspace.functions.referencingUserType(name);
+        if (!isEmpty(functions))
+        {
+            throw ire("Cannot drop user type '%s.%s' as it is still used by functions %s",
+                      keyspaceName,
+                      typeName,
+                      join(", ", transform(functions, f -> f.name().toString())));
+        }
+
+        Iterable<UserType> types = keyspace.types.referencingUserType(name);
+        if (!isEmpty(types))
+        {
+            throw ire("Cannot drop user type '%s.%s' as it is still used by user types %s",
+                      keyspaceName,
+                      typeName,
+                      join(", ", transform(types, UserType::getNameAsString)));
+
+        }
+
+        Iterable<TableMetadata> tables = keyspace.tables.referencingUserType(name);
+        if (!isEmpty(tables))
+        {
+            throw ire("Cannot drop user type '%s.%s' as it is still used by tables %s",
+                      keyspaceName,
+                      typeName,
+                      join(", ", transform(tables, t -> t.name)));
+        }
+
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.types.without(type)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.DROPPED, Target.TYPE, keyspaceName, typeName);
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureKeyspacePermission(keyspaceName, Permission.DROP);
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final UTName name;
+        private final boolean ifExists;
+
+        public Raw(UTName name, boolean ifExists)
+        {
+            this.name = name;
+            this.ifExists = ifExists;
+        }
+
+        public DropTypeStatement prepare(ClientState state)
+        {
+            String keyspaceName = name.hasKeyspace() ? name.getKeyspace() : state.getKeyspace();
+            return new DropTypeStatement(keyspaceName, name.getStringTypeName(), ifExists);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/DropViewStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropViewStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropViewStatement.java
new file mode 100644
index 0000000..e154dcb
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropViewStatement.java
@@ -0,0 +1,90 @@
+/*
+ * 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.schema;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.QualifiedName;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+import org.apache.cassandra.transport.Event.SchemaChange.Target;
+
+public final class DropViewStatement extends AlterSchemaStatement
+{
+    private final String viewName;
+    private final boolean ifExists;
+
+    public DropViewStatement(String keyspaceName, String viewName, boolean ifExists)
+    {
+        super(keyspaceName);
+        this.viewName = viewName;
+        this.ifExists = ifExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+
+        ViewMetadata view = null == keyspace
+                          ? null
+                          : keyspace.views.getNullable(viewName);
+
+        if (null == view)
+        {
+            if (ifExists)
+                return schema;
+
+            throw ire("Materialized view '%s.%s' doesn't exist", keyspaceName, viewName);
+        }
+
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.views.without(viewName)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.DROPPED, Target.TABLE, keyspaceName, viewName);
+    }
+
+    public void authorize(ClientState client)
+    {
+        ViewMetadata view = Schema.instance.getView(keyspaceName, viewName);
+        if (null != view)
+            client.ensureTablePermission(keyspaceName, view.baseTableName, Permission.ALTER);
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final QualifiedName name;
+        private final boolean ifExists;
+
+        public Raw(QualifiedName name, boolean ifExists)
+        {
+            this.name = name;
+            this.ifExists = ifExists;
+        }
+
+        public DropViewStatement prepare(ClientState state)
+        {
+            String keyspaceName = name.hasKeyspace() ? name.getKeyspace() : state.getKeyspace();
+            return new DropViewStatement(keyspaceName, name.getName(), ifExists);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/db/Keyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/Keyspace.java b/src/java/org/apache/cassandra/db/Keyspace.java
index f0fd5aa..7d1d8da 100644
--- a/src/java/org/apache/cassandra/db/Keyspace.java
+++ b/src/java/org/apache/cassandra/db/Keyspace.java
@@ -51,7 +51,6 @@ import org.apache.cassandra.schema.SchemaConstants;
 import org.apache.cassandra.schema.TableId;
 import org.apache.cassandra.schema.TableMetadata;
 import org.apache.cassandra.schema.TableMetadataRef;
-import org.apache.cassandra.service.StorageService;
 import org.apache.cassandra.tracing.Tracing;
 import org.apache.cassandra.utils.*;
 import org.apache.cassandra.utils.concurrent.OpOrder;
@@ -350,11 +349,7 @@ public class Keyspace
 
     private void createReplicationStrategy(KeyspaceMetadata ksm)
     {
-        replicationStrategy = AbstractReplicationStrategy.createReplicationStrategy(ksm.name,
-                                                                                    ksm.params.replication.klass,
-                                                                                    StorageService.instance.getTokenMetadata(),
-                                                                                    DatabaseDescriptor.getEndpointSnitch(),
-                                                                                    ksm.params.replication.options);
+        replicationStrategy = ksm.createReplicationStrategy();
     }
 
     // best invoked on the compaction mananger.

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/db/SystemKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/SystemKeyspace.java b/src/java/org/apache/cassandra/db/SystemKeyspace.java
index 0d64a94..d0ca695 100644
--- a/src/java/org/apache/cassandra/db/SystemKeyspace.java
+++ b/src/java/org/apache/cassandra/db/SystemKeyspace.java
@@ -42,7 +42,7 @@ import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.cql3.QueryProcessor;
 import org.apache.cassandra.cql3.UntypedResultSet;
 import org.apache.cassandra.cql3.functions.*;
-import org.apache.cassandra.cql3.statements.CreateTableStatement;
+import org.apache.cassandra.cql3.statements.schema.CreateTableStatement;
 import org.apache.cassandra.db.commitlog.CommitLogPosition;
 import org.apache.cassandra.db.compaction.CompactionHistoryTabularData;
 import org.apache.cassandra.db.marshal.*;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


Mime
View raw message