cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From alek...@apache.org
Subject [06/11] cassandra git commit: 13426: work in progress
Date Wed, 31 May 2017 17:51:45 GMT
http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
index 7a2a1ba..deaf375 100644
--- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
@@ -47,19 +47,14 @@ public class UpdateStatement extends ModificationStatement
     private static final Constants.Value EMPTY = new Constants.Value(ByteBufferUtil.EMPTY_BYTE_BUFFER);
 
     private UpdateStatement(StatementType type,
-                            int boundTerms,
+                            VariableSpecifications bindVariables,
                             TableMetadata metadata,
                             Operations operations,
                             StatementRestrictions restrictions,
                             Conditions conditions,
                             Attributes attrs)
     {
-        super(type, boundTerms, metadata, operations, restrictions, conditions, attrs);
-    }
-
-    public boolean requireFullClusteringKey()
-    {
-        return true;
+        super(type, bindVariables, metadata, operations, restrictions, conditions, attrs);
     }
 
     @Override
@@ -124,7 +119,7 @@ public class UpdateStatement extends ModificationStatement
          * @param columnValues list of column values (corresponds to names)
          * @param ifNotExists true if an IF NOT EXISTS condition was specified, false otherwise
          */
-        public ParsedInsert(CFName name,
+        public ParsedInsert(QualifiedName name,
                             Attributes.Raw attrs,
                             List<ColumnMetadata.Raw> columnNames,
                             List<Term.Raw> columnValues,
@@ -137,7 +132,7 @@ public class UpdateStatement extends ModificationStatement
 
         @Override
         protected ModificationStatement prepareInternal(TableMetadata metadata,
-                                                        VariableSpecifications boundNames,
+                                                        VariableSpecifications bindVariables,
                                                         Conditions conditions,
                                                         Attributes attrs)
         {
@@ -170,7 +165,7 @@ public class UpdateStatement extends ModificationStatement
                 else
                 {
                     Operation operation = new Operation.SetValue(value).prepare(metadata, def);
-                    operation.collectMarkerSpecification(boundNames);
+                    operation.collectMarkerSpecification(bindVariables);
                     operations.add(operation);
                 }
             }
@@ -180,13 +175,13 @@ public class UpdateStatement extends ModificationStatement
             StatementRestrictions restrictions = new StatementRestrictions(type,
                                                                            metadata,
                                                                            whereClause.build(),
-                                                                           boundNames,
+                                                                           bindVariables,
                                                                            applyOnlyToStaticColumns,
                                                                            false,
                                                                            false);
 
             return new UpdateStatement(type,
-                                       boundNames.size(),
+                                       bindVariables,
                                        metadata,
                                        operations,
                                        restrictions,
@@ -203,7 +198,7 @@ public class UpdateStatement extends ModificationStatement
         private final Json.Raw jsonValue;
         private final boolean defaultUnset;
 
-        public ParsedInsertJson(CFName name, Attributes.Raw attrs, Json.Raw jsonValue, boolean defaultUnset, boolean ifNotExists)
+        public ParsedInsertJson(QualifiedName name, Attributes.Raw attrs, Json.Raw jsonValue, boolean defaultUnset, boolean ifNotExists)
         {
             super(name, StatementType.INSERT, attrs, null, ifNotExists, false);
             this.jsonValue = jsonValue;
@@ -212,14 +207,14 @@ public class UpdateStatement extends ModificationStatement
 
         @Override
         protected ModificationStatement prepareInternal(TableMetadata metadata,
-                                                        VariableSpecifications boundNames,
+                                                        VariableSpecifications bindVariables,
                                                         Conditions conditions,
                                                         Attributes attrs)
         {
             checkFalse(metadata.isCounter(), "INSERT statements are not allowed on counter tables, use UPDATE instead");
 
             Collection<ColumnMetadata> defs = metadata.columns();
-            Json.Prepared prepared = jsonValue.prepareAndCollectMarkers(metadata, defs, boundNames);
+            Json.Prepared prepared = jsonValue.prepareAndCollectMarkers(metadata, defs, bindVariables);
 
             WhereClause.Builder whereClause = new WhereClause.Builder();
             Operations operations = new Operations(type);
@@ -238,7 +233,7 @@ public class UpdateStatement extends ModificationStatement
                 else
                 {
                     Operation operation = new Operation.SetValue(raw).prepare(metadata, def);
-                    operation.collectMarkerSpecification(boundNames);
+                    operation.collectMarkerSpecification(bindVariables);
                     operations.add(operation);
                 }
             }
@@ -248,13 +243,13 @@ public class UpdateStatement extends ModificationStatement
             StatementRestrictions restrictions = new StatementRestrictions(type,
                                                                            metadata,
                                                                            whereClause.build(),
-                                                                           boundNames,
+                                                                           bindVariables,
                                                                            applyOnlyToStaticColumns,
                                                                            false,
                                                                            false);
 
             return new UpdateStatement(type,
-                                       boundNames.size(),
+                                       bindVariables,
                                        metadata,
                                        operations,
                                        restrictions,
@@ -279,7 +274,7 @@ public class UpdateStatement extends ModificationStatement
          * @param whereClause the where clause
          * @param ifExists flag to check if row exists
          * */
-        public ParsedUpdate(CFName name,
+        public ParsedUpdate(QualifiedName name,
                             Attributes.Raw attrs,
                             List<Pair<ColumnMetadata.Raw, Operation.RawUpdate>> updates,
                             WhereClause whereClause,
@@ -293,7 +288,7 @@ public class UpdateStatement extends ModificationStatement
 
         @Override
         protected ModificationStatement prepareInternal(TableMetadata metadata,
-                                                        VariableSpecifications boundNames,
+                                                        VariableSpecifications bindVariables,
                                                         Conditions conditions,
                                                         Attributes attrs)
         {
@@ -306,18 +301,18 @@ public class UpdateStatement extends ModificationStatement
                 checkFalse(def.isPrimaryKeyColumn(), "PRIMARY KEY part %s found in SET part", def.name);
 
                 Operation operation = entry.right.prepare(metadata, def);
-                operation.collectMarkerSpecification(boundNames);
+                operation.collectMarkerSpecification(bindVariables);
                 operations.add(operation);
             }
 
             StatementRestrictions restrictions = newRestrictions(metadata,
-                                                                 boundNames,
+                                                                 bindVariables,
                                                                  operations,
                                                                  whereClause,
                                                                  conditions);
 
             return new UpdateStatement(type,
-                                       boundNames.size(),
+                                       bindVariables,
                                        metadata,
                                        operations,
                                        restrictions,

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/UseStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/UseStatement.java b/src/java/org/apache/cassandra/cql3/statements/UseStatement.java
index 02a678a..d48ff62 100644
--- a/src/java/org/apache/cassandra/cql3/statements/UseStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UseStatement.java
@@ -25,7 +25,7 @@ import org.apache.cassandra.transport.messages.ResultMessage;
 import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.QueryState;
 
-public class UseStatement extends ParsedStatement implements CQLStatement
+public class UseStatement extends CQLStatement.Raw implements CQLStatement
 {
     private final String keyspace;
 
@@ -34,17 +34,12 @@ public class UseStatement extends ParsedStatement implements CQLStatement
         this.keyspace = keyspace;
     }
 
-    public int getBoundTerms()
+    public UseStatement prepare(ClientState state)
     {
-        return 0;
+        return this;
     }
 
-    public Prepared prepare() throws InvalidRequestException
-    {
-        return new Prepared(this);
-    }
-
-    public void checkAccess(ClientState state) throws UnauthorizedException
+    public void authorize(ClientState state) throws UnauthorizedException
     {
         state.validateLogin();
     }
@@ -59,7 +54,7 @@ public class UseStatement extends ParsedStatement implements CQLStatement
         return new ResultMessage.SetKeyspace(keyspace);
     }
 
-    public ResultMessage executeInternal(QueryState state, QueryOptions options) throws InvalidRequestException
+    public ResultMessage executeLocally(QueryState state, QueryOptions options) throws InvalidRequestException
     {
         // In production, internal queries are exclusively on the system keyspace and 'use' is thus useless
         // but for some unit tests we need to set the keyspace (e.g. for tests with DROP INDEX)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java
new file mode 100644
index 0000000..bbd5746
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.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 java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.statements.KeyspaceAttributes;
+import org.apache.cassandra.locator.AbstractReplicationStrategy;
+import org.apache.cassandra.locator.LocalStrategy;
+import org.apache.cassandra.schema.KeyspaceMetadata;
+import org.apache.cassandra.schema.KeyspaceMetadata.KeyspaceDiff;
+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 AlterKeyspaceStatement extends AlterSchemaStatement
+{
+    private final KeyspaceAttributes attrs;
+
+    public AlterKeyspaceStatement(String keyspaceName, KeyspaceAttributes attrs)
+    {
+        super(keyspaceName);
+        this.attrs = attrs;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        attrs.validate();
+
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+        if (null == keyspace)
+            throw ire("Keyspace '%s' doesn't exist", keyspaceName);
+
+        KeyspaceMetadata newKeyspace = keyspace.withSwapped(attrs.asAlteredKeyspaceParams(keyspace.params));
+
+        if (newKeyspace.params.replication.klass.equals(LocalStrategy.class))
+            throw ire("Unable to use given strategy class: LocalStrategy is reserved for internal use.");
+
+        newKeyspace.params.validate(keyspaceName);
+
+        return schema.withAddedOrUpdated(newKeyspace);
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.UPDATED, keyspaceName);
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureKeyspacePermission(keyspaceName, Permission.ALTER);
+    }
+
+    @Override
+    Set<String> clientWarnings(KeyspacesDiff diff)
+    {
+        KeyspaceDiff keyspaceDiff = diff.altered.get(0);
+
+        AbstractReplicationStrategy before = keyspaceDiff.before.createReplicationStrategy();
+        AbstractReplicationStrategy after = keyspaceDiff.after.createReplicationStrategy();
+
+        return before.getReplicationFactor() < after.getReplicationFactor()
+             ? ImmutableSet.of("When increasing replication factor you need to run a full (-full) repair to distribute the data.")
+             : ImmutableSet.of();
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final String keyspaceName;
+        private final KeyspaceAttributes attrs;
+
+        public Raw(String keyspaceName, KeyspaceAttributes attrs)
+        {
+            this.keyspaceName = keyspaceName;
+            this.attrs = attrs;
+        }
+
+        public AlterKeyspaceStatement prepare(ClientState state)
+        {
+            return new AlterKeyspaceStatement(keyspaceName, attrs);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/AlterSchemaStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/AlterSchemaStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/AlterSchemaStatement.java
new file mode 100644
index 0000000..1776bf1
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/AlterSchemaStatement.java
@@ -0,0 +1,161 @@
+/*
+ * 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.Set;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.apache.cassandra.auth.AuthenticatedUser;
+import org.apache.cassandra.auth.IResource;
+import org.apache.cassandra.auth.RoleResource;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.schema.MigrationManager;
+import org.apache.cassandra.schema.SchemaConstants;
+import org.apache.cassandra.schema.SchemaTransformation;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.service.ClientWarn;
+import org.apache.cassandra.service.QueryState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.messages.ResultMessage;
+import org.apache.cassandra.utils.FBUtilities;
+
+abstract class AlterSchemaStatement implements CQLStatement, SchemaTransformation
+{
+    protected final String keyspaceName; // name of the keyspace affected by the statement
+
+    protected AlterSchemaStatement(String keyspaceName)
+    {
+        this.keyspaceName = keyspaceName;
+    }
+
+    public void validate(ClientState state)
+    {
+        // no-op; validation is performed while executing the statement, in apply()
+    }
+
+    public void authorize(ClientState client)
+    {
+        // TODO: drop default impl
+    }
+
+    public ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime)
+    {
+        return execute(state, false);
+    }
+
+    public ResultMessage executeLocally(QueryState state, QueryOptions options)
+    {
+        return execute(state, true);
+    }
+
+    /**
+     * TODO: drop default impl, document
+     */
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return null;
+    }
+
+    /**
+     * Schema alteration may result in a new database object (keyspace, table, role, function) being created capable of
+     * having permissions GRANTed on it. The creator of the object (the primary role assigned to the AuthenticatedUser
+     * performing the operation) is automatically granted ALL applicable permissions on the object. This is a hook for
+     * subclasses to override in order indicate which resources to to perform that grant on when the statement is executed.
+     *
+     * Only called if the transformation resulted in a non-empty diff.
+     */
+    Set<IResource> createdResources(KeyspacesDiff diff)
+    {
+        return ImmutableSet.of();
+    }
+
+    /**
+     * Schema alteration might produce a client warning (e.g. a warning to run full repair when increading RF of a keyspace).
+     * This method should be used to generate them instead of calling warn() in transformation code.
+     *
+     * Only called if the transformation resulted in a non-empty diff.
+     */
+    Set<String> clientWarnings(KeyspacesDiff diff)
+    {
+        return ImmutableSet.of();
+    }
+
+    public ResultMessage execute(QueryState state, boolean locally)
+    {
+        if (SchemaConstants.isSystemKeyspace(keyspaceName))
+            throw ire("System keyspace '%s' is not user-modifiable", keyspaceName);
+
+        validateKeyspaceName();
+
+        KeyspacesDiff diff = MigrationManager.alterSchema(this, FBUtilities.timestampMicros(), locally);
+
+        if (diff.isEmpty())
+            return new ResultMessage.Void();
+
+        /*
+         * When a schema alteration results in a new db object being created, we grant permissions on the new
+         * object to the user performing the request if:
+         * - the user is not anonymous
+         * - the configured IAuthorizer supports granting of permissions (not all do, AllowAllAuthorizer doesn't and
+         *   custom external implementations may not)
+         */
+        AuthenticatedUser user = state.getClientState().getUser();
+        if (null != user && !user.isAnonymous())
+            createdResources(diff).forEach(r -> grantPermissionsOnResource(r, user));
+
+        clientWarnings(diff).forEach(ClientWarn.instance::warn);
+
+        return new ResultMessage.SchemaChange(schemaChangeEvent(diff));
+    }
+
+    private void validateKeyspaceName()
+    {
+        if (!SchemaConstants.isValidName(keyspaceName))
+        {
+            throw ire("Keyspace name must not be empty, more than %d characters long, " +
+                      "or contain non-alphanumeric-underscore characters (got '%s')",
+                      SchemaConstants.NAME_LENGTH, keyspaceName);
+        }
+    }
+
+    private void grantPermissionsOnResource(IResource resource, AuthenticatedUser user)
+    {
+        try
+        {
+            DatabaseDescriptor.getAuthorizer()
+                              .grant(AuthenticatedUser.SYSTEM_USER,
+                                     resource.applicablePermissions(),
+                                     resource,
+                                     RoleResource.role(user.getName()));
+        }
+        catch (UnsupportedOperationException e)
+        {
+            // not a problem - grant is an optional method on IAuthorizer
+        }
+    }
+
+    static InvalidRequestException ire(String format, Object... args)
+    {
+        return new InvalidRequestException(String.format(format, args));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java
new file mode 100644
index 0000000..f0cd58d
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java
@@ -0,0 +1,341 @@
+/*
+ * 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.Map;
+import java.util.Set;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQL3Type;
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.cql3.statements.TableAttributes;
+import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.IndexMetadata;
+import org.apache.cassandra.schema.KeyspaceMetadata;
+import org.apache.cassandra.schema.Keyspaces;
+import org.apache.cassandra.schema.TableMetadata;
+import org.apache.cassandra.schema.TableParams;
+import org.apache.cassandra.schema.ViewMetadata;
+import org.apache.cassandra.schema.Views;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+
+import static java.lang.String.join;
+
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.isEmpty;
+import static com.google.common.collect.Iterables.transform;
+
+public abstract class AlterTableStatement extends AlterSchemaStatement
+{
+    protected final String tableName;
+
+    public AlterTableStatement(String keyspaceName, String tableName)
+    {
+        super(keyspaceName);
+        this.tableName = tableName;
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureTablePermission(keyspaceName, tableName, Permission.ALTER);
+    }
+
+    SchemaChange schemaChangeEvent(Keyspaces.KeyspacesDiff diff)
+    {
+        return new SchemaChange(SchemaChange.Change.UPDATED, SchemaChange.Target.TABLE, keyspaceName, tableName);
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+
+        TableMetadata table = null == keyspace
+                            ? null
+                            : keyspace.getTableOrViewNullable(tableName);
+
+        if (null == table)
+            throw ire("Table '%s.%s' doesn't exist", keyspaceName, tableName);
+
+        if (table.isView())
+            throw ire("Cannot use ALTER TABLE on a materialized view; use ALTER MATERIALIZED VIEW instead");
+
+        return schema.withAddedOrUpdated(apply(keyspace, table));
+    }
+
+    abstract KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table);
+
+    public static class AddColumns extends AlterTableStatement
+    {
+        private final Collection<ColumnAdded> newColumns;
+
+        public AddColumns(String keyspaceName, String tableName, Collection<ColumnAdded> newColumns)
+        {
+            super(keyspaceName, tableName);
+            this.newColumns = newColumns;
+        }
+
+        public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
+        {
+            if (table.isCompactTable())
+                throw ire("Cannot add new column to a COMPACT STORAGE table");
+
+            TableMetadata.Builder tableBuilder = table.unbuild();
+            Views.Builder viewsBuilder = keyspace.views.unbuild();
+            newColumns.forEach(c -> addColumn(keyspace, table, c, tableBuilder, viewsBuilder));
+
+            return keyspace.withSwapped(keyspace.tables.withSwapped(tableBuilder.build()))
+                           .withSwapped(viewsBuilder.build());
+        }
+
+        private void addColumn(KeyspaceMetadata keyspace,
+                               TableMetadata table,
+                               ColumnAdded column,
+                               TableMetadata.Builder tableBuilder,
+                               Views.Builder viewsBuilder)
+        {
+            ColumnIdentifier name = column.name.getIdentifier(table);
+            AbstractType<?> type = column.type.prepare(keyspaceName, keyspace.types).getType();
+            boolean isStatic = column.isStatic;
+
+            if (null != tableBuilder.getColumn(name))
+                throw ire("Column with name '%s' already exists", name);
+
+            if (isStatic && table.clusteringColumns().isEmpty())
+                throw ire("Static columns are only useful (and thus allowed) if the table has at least one clustering column");
+
+            ColumnMetadata droppedColumn = table.getDroppedColumn(name.bytes);
+            if (null != droppedColumn)
+            {
+                // After #8099, not safe to re-add columns of incompatible types - until *maybe* deser logic with dropped
+                // columns is pushed deeper down the line. The latter would still be problematic in cases of schema races.
+                if (!droppedColumn.type.isValueCompatibleWith(type))
+                {
+                    throw ire("Cannot re-add a previously dropped column '%s' of type %s, incompatible with previous type %s",
+                              name,
+                              type.asCQL3Type(),
+                              droppedColumn.type.asCQL3Type());
+                }
+
+                // Cannot re-add a dropped counter column. See #7831.
+                if (table.isCounter())
+                    throw ire("Cannot re-add previously dropped counter column %s", name);
+            }
+
+            if (type.isCollection() && type.isMultiCell())
+            {
+                if (table.isCompactTable())
+                    throw ire("Cannot use non-frozen collections in COMPACT STORAGE tables");
+
+                if (table.isSuper())
+                    throw ire("Cannot use non-frozen collections with super column families");
+            }
+
+            if (isStatic)
+                tableBuilder.addStaticColumn(name, type);
+            else
+                tableBuilder.addRegularColumn(name, type);
+
+            if (!isStatic)
+            {
+                for (ViewMetadata view : keyspace.views.forTable(table.id))
+                {
+                    if (view.includeAllColumns)
+                    {
+                        ColumnMetadata viewColumn = ColumnMetadata.regularColumn(view.metadata, name.bytes, type);
+                        viewsBuilder.put(viewsBuilder.get(view.name()).withAddedRegularColumn(viewColumn));
+                    }
+                }
+            }
+        }
+    }
+
+    public static class DropColumns extends AlterTableStatement
+    {
+        private final Collection<ColumnMetadata.Raw> removedColumns;
+        private final long timestamp;
+
+        public DropColumns(String keyspaceName, String tableName, Collection<ColumnMetadata.Raw> removedColumns, long timestamp)
+        {
+            super(keyspaceName, tableName);
+            this.removedColumns = removedColumns;
+            this.timestamp = timestamp;
+        }
+
+        public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
+        {
+            if (!table.isCQLTable())
+                throw ire("Cannot drop columns from a non-CQL3 table");
+
+            TableMetadata.Builder builder = table.unbuild();
+            removedColumns.forEach(c -> dropColumn(keyspace, table, c, builder));
+
+            return keyspace.withSwapped(keyspace.tables.withSwapped(builder.build()));
+        }
+
+        private void dropColumn(KeyspaceMetadata keyspace, TableMetadata table, ColumnMetadata.Raw column, TableMetadata.Builder builder)
+        {
+            ColumnIdentifier name = column.getIdentifier(table);
+
+            ColumnMetadata currentColumn = table.getColumn(name);
+            if (null == currentColumn)
+                throw ire("Column %s was not found in table '%s'", name, table);
+
+            if (currentColumn.isPrimaryKeyColumn())
+                throw ire("Cannot drop PRIMARY KEY column %s", name);
+
+            // TODO: some day try and find a way to not rely on Keyspace/IndexManager/Index to find dependent indexes
+            Set<IndexMetadata> dependentIndexes = Keyspace.openAndGetStore(table).indexManager.getDependentIndexes(currentColumn);
+            if (!dependentIndexes.isEmpty())
+            {
+                throw ire("Cannot drop column %s because it has dependent secondary indexes (%s)",
+                          currentColumn,
+                          join(", ", transform(dependentIndexes, i -> i.name)));
+            }
+
+            Iterable<ViewMetadata> dependentViews = filter(keyspace.views.forTable(table.id), v -> v.includes(name));
+            if (!isEmpty(dependentViews))
+            {
+                throw ire("Cannot drop column %s because it has dependent materialized views (%s)",
+                          currentColumn,
+                          join(", ", transform(dependentViews, ViewMetadata::name)));
+            }
+
+            builder.removeRegularOrStaticColumn(name);
+            builder.recordColumnDrop(currentColumn, timestamp);
+        }
+    }
+
+    public static class RenameColumns extends AlterTableStatement
+    {
+        private final Map<ColumnMetadata.Raw, ColumnMetadata.Raw> renamedColumns;
+
+        public RenameColumns(String keyspaceName, String tableName, Map<ColumnMetadata.Raw, ColumnMetadata.Raw> renamedColumns)
+        {
+            super(keyspaceName, tableName);
+            this.renamedColumns = renamedColumns;
+        }
+
+        public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
+        {
+            TableMetadata.Builder tableBuilder = table.unbuild();
+            Views.Builder viewsBuilder = keyspace.views.unbuild();
+            renamedColumns.forEach((o, n) -> renameColumn(keyspace, table, o, n, tableBuilder, viewsBuilder));
+
+            return keyspace.withSwapped(keyspace.tables.withSwapped(tableBuilder.build()))
+                           .withSwapped(viewsBuilder.build());
+        }
+
+        private void renameColumn(KeyspaceMetadata keyspace,
+                                  TableMetadata table,
+                                  ColumnMetadata.Raw oldName,
+                                  ColumnMetadata.Raw newName,
+                                  TableMetadata.Builder tableBuilder,
+                                  Views.Builder viewsBuilder)
+        {
+            ColumnIdentifier oldColumnName = oldName.getIdentifier(table);
+            ColumnIdentifier newColumnName = newName.getIdentifier(table);
+
+            ColumnMetadata column = table.getColumn(oldColumnName);
+            if (null == column)
+                throw ire("Column %s was not found in table %s", oldColumnName, table);
+
+            if (!column.isPrimaryKeyColumn())
+                throw ire("Cannot rename non PRIMARY KEY column %s", oldColumnName);
+
+            if (null != table.getColumn(newColumnName))
+            {
+                throw ire("Cannot rename column %s to %s in table '%s'; another column with that name already exists",
+                          oldColumnName,
+                          newColumnName,
+                          table);
+            }
+
+            // TODO: some day try and find a way to not rely on Keyspace/IndexManager/Index to find dependent indexes
+            Set<IndexMetadata> dependentIndexes = Keyspace.openAndGetStore(table).indexManager.getDependentIndexes(column);
+            if (!dependentIndexes.isEmpty())
+            {
+                throw ire("Can't rename column %s because it has dependent secondary indexes (%s)",
+                          oldColumnName,
+                          join(", ", transform(dependentIndexes, i -> i.name)));
+            }
+
+            for (ViewMetadata view : keyspace.views.forTable(table.id))
+            {
+                if (view.includes(oldColumnName))
+                {
+                    ColumnIdentifier oldViewColumn = oldName.getIdentifier(view.metadata);
+                    ColumnIdentifier newViewColumn = newName.getIdentifier(view.metadata);
+
+                    viewsBuilder.put(viewsBuilder.get(view.name()).withRenamedPrimaryKeyColumn(oldViewColumn, newViewColumn));
+                }
+            }
+
+            tableBuilder.renamePrimaryKeyColumn(oldColumnName, newColumnName);
+        }
+    }
+
+    public static class AlterOptions extends AlterTableStatement
+    {
+        private final TableAttributes attrs;
+
+        public AlterOptions(String keyspaceName, String tableName, TableAttributes attrs)
+        {
+            super(keyspaceName, tableName);
+            this.attrs = attrs;
+        }
+
+        public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
+        {
+            attrs.validate();
+
+            TableParams params = attrs.asAlteredTableParams(table.params);
+
+            if (table.isCounter() && params.defaultTimeToLive > 0)
+                throw ire("Cannot set default_time_to_live on a table with counters");
+
+            if (!isEmpty(keyspace.views.forTable(table.id)) && params.gcGraceSeconds == 0)
+            {
+                throw ire("Cannot alter gc_grace_seconds of the base table of a " +
+                          "materialized view to 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.");
+            }
+
+            return keyspace.withSwapped(keyspace.tables.withSwapped(table.withSwapped(params)));
+        }
+    }
+
+    public static class ColumnAdded
+    {
+        final ColumnMetadata.Raw name;
+        final CQL3Type.Raw type;
+        final boolean isStatic;
+
+        ColumnAdded(ColumnMetadata.Raw name, CQL3Type.Raw type, boolean isStatic)
+        {
+            this.name = name;
+            this.type = type;
+            this.isStatic = isStatic;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java
new file mode 100644
index 0000000..42adfe0
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java
@@ -0,0 +1,149 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQL3Type;
+import org.apache.cassandra.cql3.FieldIdentifier;
+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.service.ClientState;
+import org.apache.cassandra.transport.Event.SchemaChange;
+
+import static java.lang.String.join;
+import static java.util.function.Predicate.isEqual;
+import static java.util.stream.Collectors.toList;
+
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+
+public abstract class AlterTypeStatement extends AlterSchemaStatement
+{
+    protected final String typeName;
+
+    public AlterTypeStatement(String keyspaceName, String typeName)
+    {
+        super(keyspaceName);
+        this.typeName = typeName;
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureKeyspacePermission(keyspaceName, Permission.ALTER);
+    }
+
+    SchemaChange schemaChangeEvent(Keyspaces.KeyspacesDiff diff)
+    {
+        return new SchemaChange(SchemaChange.Change.UPDATED, SchemaChange.Target.TYPE, keyspaceName, typeName);
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+
+        UserType type = null == keyspace
+                      ? null
+                      : keyspace.types.getNullable(bytes(typeName));
+
+        if (null == type)
+            throw ire("Type '%s.%s' doesn't exist", keyspaceName, typeName);
+
+        return schema.withAddedOrUpdated(keyspace.withUpdatedUserType(apply(keyspace, type)));
+    }
+
+    abstract UserType apply(KeyspaceMetadata keyspace, UserType type);
+
+    public static final class AddField extends AlterTypeStatement
+    {
+        private final FieldIdentifier fieldName;
+        private final CQL3Type.Raw type;
+
+        public AddField(String keyspaceName, String typeName, FieldIdentifier fieldName, CQL3Type.Raw type)
+        {
+            super(keyspaceName, typeName);
+            this.fieldName = fieldName;
+            this.type = type;
+        }
+
+        UserType apply(KeyspaceMetadata keyspace, UserType userType)
+        {
+            if (userType.fieldPosition(fieldName) >= 0)
+                throw ire("Cannot add field %s to type %s: a field with name %s already exists", fieldName, userType, fieldName);
+
+            AbstractType<?> fieldType = type.prepare(keyspaceName, keyspace.types).getType();
+            if (fieldType.referencesUserType(userType.name))
+                throw ire("Cannot add new field %s of type %s to user type %s as it would create a circular reference", fieldName, type, userType);
+
+            List<FieldIdentifier> fieldNames = new ArrayList<>(userType.fieldNames()); fieldNames.add(fieldName);
+            List<AbstractType<?>> fieldTypes = new ArrayList<>(userType.fieldTypes()); fieldTypes.add(fieldType);
+
+            return new UserType(keyspaceName, userType.name, fieldNames, fieldTypes, userType.isMultiCell());
+        }
+    }
+
+    public static final class RenameFields extends AlterTypeStatement
+    {
+        private final Map<FieldIdentifier, FieldIdentifier> renamedFields;
+
+        public RenameFields(String keyspaceName, String typeName, Map<FieldIdentifier, FieldIdentifier> renamedFields)
+        {
+            super(keyspaceName, typeName);
+            this.renamedFields = renamedFields;
+        }
+
+        UserType apply(KeyspaceMetadata keyspace, UserType userType)
+        {
+            List<String> dependentAggregates =
+                keyspace.functions
+                        .udas()
+                        .filter(uda -> null != uda.initialCondition() && uda.stateType().referencesUserType(userType.name))
+                        .map(uda -> uda.name().toString())
+                        .collect(toList());
+
+            if (!dependentAggregates.isEmpty())
+            {
+                throw ire("Cannot alter user type %s as it is still used in INITCOND by aggregates %s",
+                          typeName,
+                          join(", ", dependentAggregates));
+            }
+
+            List<FieldIdentifier> fieldNames = new ArrayList<>(userType.fieldNames());
+
+            renamedFields.forEach((oldName, newName) ->
+            {
+                int idx = userType.fieldPosition(oldName);
+                if (idx < 0)
+                    throw ire("Unkown field %s in type %s", oldName, typeName);
+                fieldNames.set(idx, newName);
+            });
+
+            fieldNames.forEach(name ->
+            {
+                if (fieldNames.stream().filter(isEqual(name)).count() > 1)
+                    throw ire("Duplicate field name %s in type %s", name, typeName);
+            });
+
+            return new UserType(keyspaceName, userType.name, fieldNames, userType.fieldTypes(), userType.isMultiCell());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/AlterViewStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/AlterViewStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/AlterViewStatement.java
new file mode 100644
index 0000000..2de352e
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/AlterViewStatement.java
@@ -0,0 +1,105 @@
+/*
+ * 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.cql3.statements.TableAttributes;
+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 AlterViewStatement extends AlterSchemaStatement
+{
+    private final String viewName;
+    private final TableAttributes attrs;
+
+    public AlterViewStatement(String keyspaceName, String viewName, TableAttributes attrs)
+    {
+        super(keyspaceName);
+        this.viewName = viewName;
+        this.attrs = attrs;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+
+        ViewMetadata view = null == keyspace
+                          ? null
+                          : keyspace.views.getNullable(viewName);
+
+        if (null == view)
+            throw ire("Materialized view '%s.%s' doesn't exist", keyspaceName, viewName);
+
+        attrs.validate();
+
+        TableParams params = attrs.asAlteredTableParams(view.metadata.params);
+
+        if (params.gcGraceSeconds == 0)
+        {
+            throw ire("Cannot alter gc_grace_seconds of a materialized view to 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.");
+        }
+
+        if (params.defaultTimeToLive > 0)
+        {
+            throw ire("Cannot set or alter 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.");
+        }
+
+        ViewMetadata newView = view.copy(view.metadata.withSwapped(params));
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.views.withSwapped(newView)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.UPDATED, 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 TableAttributes attrs;
+
+        public Raw(QualifiedName name, TableAttributes attrs)
+        {
+            this.name = name;
+            this.attrs = attrs;
+        }
+
+        public AlterViewStatement prepare(ClientState state)
+        {
+            String keyspaceName = name.hasKeyspace() ? name.getKeyspace() : state.getKeyspace();
+            return new AlterViewStatement(keyspaceName, name.getName(), attrs);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java
new file mode 100644
index 0000000..04e3c81
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java
@@ -0,0 +1,231 @@
+/*
+ * 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 java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+import org.apache.cassandra.auth.FunctionResource;
+import org.apache.cassandra.auth.IResource;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.functions.*;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.schema.KeyspaceMetadata;
+import org.apache.cassandra.schema.Keyspaces;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.ProtocolVersion;
+
+import static java.lang.String.format;
+import static java.lang.String.join;
+import static java.util.Collections.singleton;
+import static java.util.Collections.singletonList;
+
+import static com.google.common.collect.Iterables.concat;
+import static com.google.common.collect.Iterables.transform;
+
+public final class CreateAggregateStatement extends AlterSchemaStatement
+{
+    private final String aggregateName;
+    private final List<CQL3Type.Raw> rawArgumentTypes;
+    private final CQL3Type.Raw rawStateType;
+    private final FunctionName stateFunctionName;
+    private final FunctionName finalFunctionName;
+    private final Term.Raw rawInitialValue;
+    private final boolean orReplace;
+    private final boolean ifNotExists;
+
+    public CreateAggregateStatement(String keyspaceName,
+                                    String aggregateName,
+                                    List<CQL3Type.Raw> rawArgumentTypes,
+                                    CQL3Type.Raw rawStateType,
+                                    FunctionName stateFunctionName,
+                                    FunctionName finalFunctionName,
+                                    Term.Raw rawInitialValue,
+                                    boolean orReplace,
+                                    boolean ifNotExists)
+    {
+        super(keyspaceName);
+        this.aggregateName = aggregateName;
+        this.rawArgumentTypes = rawArgumentTypes;
+        this.rawStateType = rawStateType;
+        this.stateFunctionName = stateFunctionName;
+        this.finalFunctionName = finalFunctionName;
+        this.rawInitialValue = rawInitialValue;
+        this.orReplace = orReplace;
+        this.ifNotExists = ifNotExists;
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    Set<IResource> createdResources(KeyspacesDiff diff)
+    {
+        return ImmutableSet.of(FunctionResource.functionFromCql(keyspaceName, aggregateName, rawArgumentTypes));
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        if (ifNotExists && orReplace)
+            throw ire("Cannot use both 'OR REPLACE' and 'IF NOT EXISTS' directives");
+
+        rawArgumentTypes.stream()
+                        .filter(CQL3Type.Raw::isFrozen)
+                        .findFirst()
+                        .ifPresent(t -> { throw ire("Argument '%s' cannot be frozen; remove frozen<> modifier from '%s'", t, t); });
+
+        if (rawStateType.isFrozen())
+            throw ire("State type '%s' cannot be frozen; remove frozen<> modifier from '%s'", rawStateType, rawStateType);
+
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+        if (null == keyspace)
+            throw ire("Keyspace '%s' doesn't exist", keyspaceName);
+
+        /*
+         * Resolve the state function
+         */
+
+        // TODO replace Lists.transform use
+        List<AbstractType<?>> argumentTypes = Lists.transform(rawArgumentTypes, t -> t.prepare(keyspaceName, keyspace.types).getType());
+        AbstractType<?> stateType = rawStateType.prepare(keyspaceName, keyspace.types).getType();
+        List<AbstractType<?>> stateFunctionArguments = Lists.newArrayList(concat(singleton(stateType), argumentTypes));
+
+        Function stateFunction = keyspace.functions.find(stateFunctionName, stateFunctionArguments).orElse(null);
+        if (null == stateFunction)
+            throw ire("State function %s does not exist", stateFunctionString());
+
+        if (stateFunction.isAggregate())
+            throw ire("State function %s is not a scalar function", stateFunctionString());
+
+        if (!stateFunction.returnType().equals(stateType))
+        {
+            throw ire("State function %s return type must be the same as the first argument type - check STYPE, argument and return types",
+                      stateFunctionString());
+        }
+
+        /*
+         * Resolve the final function and return type
+         */
+
+        Function finalFunction = null;
+        AbstractType<?> returnType = stateFunction.returnType();
+
+        if (null != finalFunctionName)
+        {
+            finalFunction = keyspace.functions.find(finalFunctionName, singletonList(stateType)).orElse(null);
+            if (null == finalFunction)
+                throw ire("Final function %s does not exist", finalFunctionString());
+
+            if (finalFunction.isAggregate())
+                throw ire("Final function %s is not a scalar function", finalFunctionString());
+
+            // override return type with that of the final function
+            returnType = finalFunction.returnType();
+        }
+
+        /*
+         * Validate initial condition
+         */
+
+        // TODO: WTF?
+        ByteBuffer initialValue = null;
+        if (null != rawInitialValue)
+        {
+            initialValue = Terms.asBytes(keyspaceName, initialValue.toString(), stateType);
+
+            if (null != initialValue)
+            {
+                try
+                {
+                    stateType.validate(initialValue);
+                }
+                catch (MarshalException e)
+                {
+                    throw ire("Invalid value for INITCOND of type %s", stateType.asCQL3Type());
+                }
+            }
+
+            // Converts initcond to a CQL literal and parse it back to avoid another CASSANDRA-11064
+            String initialValueString = stateType.asCQL3Type().toCQLLiteral(initialValue, ProtocolVersion.CURRENT);
+            assert !Terms.asBytes(keyspaceName, initialValueString, stateType).equals(initialValue);
+
+            if (Constants.NULL_LITERAL != rawInitialValue && UDHelper.isNullOrEmpty(stateType, initialValue))
+                throw ire("INITCOND must not be empty for all types except TEXT, ASCII, BLOB");
+        }
+
+        if (!((UDFunction) stateFunction).isCalledOnNullInput() && null == initialValue)
+        {
+            throw ire("Cannot create aggregate '%s' without INITCOND because state function %s does not accept 'null' arguments",
+                      aggregateName,
+                      stateFunctionName);
+        }
+
+        /*
+         * Create or replace
+         */
+
+        Function existingAggregate = keyspace.functions.find(new FunctionName(keyspaceName, aggregateName), argumentTypes).orElse(null);
+        if (null != existingAggregate)
+        {
+            if (!existingAggregate.isAggregate())
+                throw ire("Aggregate '%s' cannot replace a function", aggregateName);
+
+            if (ifNotExists)
+                return schema;
+
+            if (!orReplace)
+                throw ire("Aggregate '%s' already exists", aggregateName);
+
+            if (!returnType.isCompatibleWith(existingAggregate.returnType()))
+            {
+                throw ire("Cannot replace aggregate '%s', the new return type %s is not compatible with the return type %s of existing function",
+                          aggregateName,
+                          returnType.asCQL3Type(),
+                          existingAggregate.returnType().asCQL3Type());
+            }
+        }
+
+        UDAggregate aggregate =
+            new UDAggregate(new FunctionName(keyspaceName, aggregateName),
+                            argumentTypes,
+                            returnType,
+                            (ScalarFunction) stateFunction,
+                            (ScalarFunction) finalFunction,
+                            initialValue);
+
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.functions.withAddedOrUpdated(aggregate)));
+    }
+
+    private String stateFunctionString()
+    {
+        return format("%s(%s)", stateFunctionName, join(", ", transform(concat(singleton(rawStateType), rawArgumentTypes), Object::toString)));
+    }
+
+    private String finalFunctionString()
+    {
+        return format("%s(%s)", finalFunctionName, rawStateType);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/CreateFunctionStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateFunctionStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateFunctionStatement.java
new file mode 100644
index 0000000..00b98f5
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateFunctionStatement.java
@@ -0,0 +1,239 @@
+/*
+ * 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.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+import org.apache.cassandra.auth.FunctionResource;
+import org.apache.cassandra.auth.IResource;
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQL3Type;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.functions.FunctionName;
+import org.apache.cassandra.cql3.functions.UDFunction;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.schema.Functions.FunctionsDiff;
+import org.apache.cassandra.schema.KeyspaceMetadata;
+import org.apache.cassandra.schema.Keyspaces;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.schema.Schema;
+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.stream.Collectors.toList;
+
+public final class CreateFunctionStatement extends AlterSchemaStatement
+{
+    private final String functionName;
+    private final List<ColumnIdentifier> argumentNames;
+    private final List<CQL3Type.Raw> rawArgumentTypes;
+    private final CQL3Type.Raw rawReturnType;
+    private final boolean calledOnNullInput;
+    private final String language;
+    private final String body;
+    private final boolean orReplace;
+    private final boolean ifNotExists;
+
+    public CreateFunctionStatement(String keyspaceName,
+                                   String functionName,
+                                   List<ColumnIdentifier> argumentNames,
+                                   List<CQL3Type.Raw> rawArgumentTypes,
+                                   CQL3Type.Raw rawReturnType,
+                                   boolean calledOnNullInput,
+                                   String language,
+                                   String body,
+                                   boolean orReplace,
+                                   boolean ifNotExists)
+    {
+        super(keyspaceName);
+        this.functionName = functionName;
+        this.argumentNames = argumentNames;
+        this.rawArgumentTypes = rawArgumentTypes;
+        this.rawReturnType = rawReturnType;
+        this.calledOnNullInput = calledOnNullInput;
+        this.language = language;
+        this.body = body;
+        this.orReplace = orReplace;
+        this.ifNotExists = ifNotExists;
+    }
+
+    // TODO: replace affected aggregates !!
+    public Keyspaces apply(Keyspaces schema)
+    {
+        if (ifNotExists && orReplace)
+            throw ire("Cannot use both 'OR REPLACE' and 'IF NOT EXISTS' directives");
+
+        UDFunction.assertUdfsEnabled(language);
+
+        if (new HashSet<>(argumentNames).size() != argumentNames.size())
+            throw ire("Duplicate argument names for given function %s with argument names %s", functionName, argumentNames);
+
+        rawArgumentTypes.stream()
+                        .filter(CQL3Type.Raw::isFrozen)
+                        .findFirst()
+                        .ifPresent(t -> { throw ire("Argument '%s' cannot be frozen; remove frozen<> modifier from '%s'", t, t); });
+
+        if (rawReturnType.isFrozen())
+            throw ire("Return type '%s' cannot be frozen; remove frozen<> modifier from '%s'", rawReturnType, rawReturnType);
+
+        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
+        if (null == keyspace)
+            throw ire("Keyspace '%s' doesn't exist", keyspaceName);
+
+        // TODO: replace Lists.transform use
+        List<AbstractType<?>> argumentTypes = Lists.transform(rawArgumentTypes, t -> t.prepare(keyspaceName, keyspace.types).getType());
+        AbstractType<?> returnType = rawReturnType.prepare(keyspaceName, keyspace.types).getType();
+
+        Function existingFunction = keyspace.functions.find(new FunctionName(keyspaceName, functionName), argumentTypes).orElse(null);
+        if (null != existingFunction)
+        {
+            if (existingFunction.isAggregate())
+                throw ire("Function '%s' cannot replace an aggregate", functionName);
+
+            if (ifNotExists)
+                return schema;
+
+            if (!orReplace)
+                throw ire("Function '%s' already exists", functionName);
+
+            if (calledOnNullInput != ((UDFunction) existingFunction).isCalledOnNullInput())
+            {
+                throw ire("Function '%s' must have %s directive",
+                          functionName,
+                          calledOnNullInput ? "CALLED ON NULL INPUT" : "RETURNS NULL ON NULL INPUT");
+            }
+
+            if (!returnType.isCompatibleWith(existingFunction.returnType()))
+            {
+                throw ire("Cannot replace function '%s', the new return type %s is not compatible with the return type %s of existing function",
+                          functionName,
+                          returnType.asCQL3Type(),
+                          existingFunction.returnType().asCQL3Type());
+            }
+        }
+
+        UDFunction function =
+            UDFunction.create(new FunctionName(keyspaceName, functionName),
+                              argumentNames,
+                              argumentTypes,
+                              returnType,
+                              calledOnNullInput,
+                              language,
+                              body);
+
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.functions.withAddedOrUpdated(function)));
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        assert diff.altered.size() == 1;
+        FunctionsDiff<UDFunction> udfsDiff = diff.altered.get(0).udfs;
+
+        assert udfsDiff.created.size() + udfsDiff.altered.size() == 1;
+        boolean created = !udfsDiff.created.isEmpty();
+
+        return new SchemaChange(created ? Change.CREATED : Change.UPDATED,
+                                Target.FUNCTION,
+                                keyspaceName,
+                                functionName,
+                                rawArgumentTypes.stream().map(CQL3Type.Raw::toString).collect(toList()));
+    }
+
+    public void authorize(ClientState client)
+    {
+        FunctionName name = new FunctionName(keyspaceName, functionName);
+
+        // TODO: replace lists.transform use
+        if (Schema.instance.findFunction(name, Lists.transform(rawArgumentTypes, t -> t.prepare(keyspaceName).getType())).isPresent() && orReplace)
+            client.ensurePermission(Permission.ALTER, FunctionResource.functionFromCql(keyspaceName, functionName, rawArgumentTypes));
+        else
+            client.ensurePermission(Permission.CREATE, FunctionResource.keyspace(keyspaceName));
+    }
+
+    @Override
+    Set<IResource> createdResources(KeyspacesDiff diff)
+    {
+        assert diff.altered.size() == 1;
+        FunctionsDiff<UDFunction> udfsDiff = diff.altered.get(0).udfs;
+
+        assert udfsDiff.created.size() + udfsDiff.altered.size() == 1;
+
+        return udfsDiff.created.isEmpty()
+             ? ImmutableSet.of()
+             : ImmutableSet.of(FunctionResource.functionFromCql(keyspaceName, functionName, rawArgumentTypes));
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final FunctionName name;
+        private final List<ColumnIdentifier> argumentNames;
+        private final List<CQL3Type.Raw> rawArgumentTypes;
+        private final CQL3Type.Raw rawReturnType;
+        private final boolean calledOnNullInput;
+        private final String language;
+        private final String body;
+        private final boolean orReplace;
+        private final boolean ifNotExists;
+
+        public Raw(FunctionName name,
+                   List<ColumnIdentifier> argumentNames,
+                   List<CQL3Type.Raw> rawArgumentTypes,
+                   CQL3Type.Raw rawReturnType,
+                   boolean calledOnNullInput,
+                   String language,
+                   String body,
+                   boolean orReplace,
+                   boolean ifNotExists)
+        {
+            this.name = name;
+            this.argumentNames = argumentNames;
+            this.rawArgumentTypes = rawArgumentTypes;
+            this.rawReturnType = rawReturnType;
+            this.calledOnNullInput = calledOnNullInput;
+            this.language = language;
+            this.body = body;
+            this.orReplace = orReplace;
+            this.ifNotExists = ifNotExists;
+        }
+
+        public CreateFunctionStatement prepare(ClientState state)
+        {
+            String keyspaceName = name.hasKeyspace() ? name.keyspace : state.getKeyspace();
+
+            return new CreateFunctionStatement(keyspaceName,
+                                               name.name,
+                                               argumentNames,
+                                               rawArgumentTypes,
+                                               rawReturnType,
+                                               calledOnNullInput,
+                                               language,
+                                               body,
+                                               orReplace,
+                                               ifNotExists);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java
new file mode 100644
index 0000000..c8849e7
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java
@@ -0,0 +1,237 @@
+/*
+ * 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.CQLStatement;
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.cql3.QualifiedName;
+import org.apache.cassandra.cql3.statements.IndexAttributes;
+import org.apache.cassandra.cql3.statements.IndexTarget;
+import org.apache.cassandra.cql3.statements.IndexTarget.Type;
+import org.apache.cassandra.db.marshal.MapType;
+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 com.google.common.collect.Iterables.tryFind;
+
+public final class CreateIndexStatement extends AlterSchemaStatement
+{
+    private final String indexName;
+    private final String tableName;
+    private final List<IndexTarget.Raw> rawIndexTargets;
+    private final IndexAttributes attrs;
+    private final boolean ifNotExists;
+
+    public CreateIndexStatement(String keyspaceName,
+                                String tableName,
+                                String indexName,
+                                List<IndexTarget.Raw> rawIndexTargets,
+                                IndexAttributes attrs,
+                                boolean ifNotExists)
+    {
+        super(keyspaceName);
+        this.tableName = tableName;
+        this.indexName = indexName;
+        this.rawIndexTargets = rawIndexTargets;
+        this.attrs = attrs;
+        this.ifNotExists = ifNotExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        attrs.validate();
+
+        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 (null != indexName && keyspace.hasIndex(indexName))
+        {
+            if (ifNotExists)
+                return schema;
+
+            throw ire("Index '%s' already exists", indexName);
+        }
+
+        if (table.isCounter())
+            throw ire("Secondary indexes on counter tables aren't supported");
+
+        if (table.isView())
+            throw ire("Secondary indexes on materialized views aren't supported");
+
+        if (table.isCompactTable() && !table.isStaticCompactTable())
+            throw ire("Secondary indexes are not supported on COMPACT STORAGE tables that have clustering columns");
+
+        List<IndexTarget> indexTargets = Lists.transform(rawIndexTargets, t -> t.prepare(table));
+
+        if (indexTargets.isEmpty() && !attrs.isCustom)
+            throw ire("Only CUSTOM indexes can be created without specifying a target column");
+
+        if (indexTargets.size() > 1)
+        {
+            if (!attrs.isCustom)
+                throw ire("Only CUSTOM indexes support multiple columns");
+
+            Set<ColumnIdentifier> columns = new HashSet<>();
+            for (IndexTarget target : indexTargets)
+                if (!columns.add(target.column))
+                    throw ire("Duplicate column '%s' in index target list", target.column);
+        }
+
+        indexTargets.forEach(t -> validateIndexTarget(table, t));
+
+        String name = null == indexName ? generateIndexName(keyspace, indexTargets) : indexName;
+
+        IndexMetadata.Kind kind;
+        if (attrs.isCustom)
+            kind = IndexMetadata.Kind.CUSTOM;
+        else
+            kind = table.isCompound() ? IndexMetadata.Kind.COMPOSITES : IndexMetadata.Kind.KEYS;
+
+        Map<String, String> options = attrs.isCustom ? attrs.getOptions() : Collections.emptyMap();
+
+        IndexMetadata index = IndexMetadata.fromIndexTargets(indexTargets, name, kind, options);
+
+        // check to disallow creation of an index which duplicates an existing one in all but name
+        IndexMetadata equalIndex = tryFind(table.indexes, i -> i.equalsWithoutName(index)).orNull();
+        if (null != equalIndex)
+        {
+            if (ifNotExists)
+                return schema;
+
+            throw ire("Index %s is a duplicate of existing index %s", index.name, equalIndex.name);
+        }
+
+        TableMetadata newTable = table.withSwapped(table.indexes.with(index));
+        newTable.validate();
+
+        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.tables.withSwapped(newTable)));
+    }
+
+    private void validateIndexTarget(TableMetadata table, IndexTarget target)
+    {
+        ColumnMetadata column = table.getColumn(target.column);
+
+        if (null == column)
+            throw ire("Column '%s' doesn't exist", target.column);
+
+        if (column.type.referencesDuration())
+        {
+            if (column.type.isCollection())
+                throw ire("Secondary indexes are not supported on collections containing durations");
+
+            if (column.type.isTuple())
+                throw ire("Secondary indexes are not supported on tuples containing durations");
+
+            if (column.type.isUDT())
+                throw  ire("Secondary indexes are not supported on UDTs containing durations");
+
+            throw ire("Secondary indexes are not supported on duration columns");
+        }
+
+        // TODO: we could lift that limitation?
+        if (table.isCompactTable() && column.isPrimaryKeyColumn())
+            throw ire("Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables");
+
+        if (column.isPartitionKey() && table.partitionKeyColumns().size() == 1)
+            throw ire("Cannot create secondary index on the only partition key column %s", column);
+
+        if (column.type.isFrozenCollection() && target.type != Type.FULL)
+            throw ire("Cannot create %s() index on frozen column %s. Frozen collections only support full() indexes", target.type, column);
+
+        if (!column.type.isFrozenCollection() && target.type == Type.FULL)
+            throw ire("full() indexes can only be created on frozen collections");
+
+        if (!column.type.isCollection() && target.type != Type.SIMPLE)
+            throw ire("Cannot create %s() index on %s. Non-collection columns only support simple indexes", target.type, column);
+
+        if (!(column.type instanceof MapType && column.type.isMultiCell()) && (target.type == Type.KEYS || target.type == Type.KEYS_AND_VALUES))
+            throw ire("Cannot create index on %s of column %s with non-map type", target.type, column);
+
+        if (column.type.isUDT() && column.type.isMultiCell())
+            throw ire("Cannot create index on non-frozen UDT column %s", column);
+    }
+
+    private String generateIndexName(KeyspaceMetadata keyspace, List<IndexTarget> targets)
+    {
+        String baseName = targets.size() == 1
+                        ? IndexMetadata.generateDefaultIndexName(tableName, targets.get(0).column)
+                        : IndexMetadata.generateDefaultIndexName(tableName);
+        return keyspace.findAvailableIndexName(baseName);
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.UPDATED, Target.TABLE, keyspaceName, tableName);
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureTablePermission(keyspaceName, tableName, Permission.ALTER);
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        private final QualifiedName tableName;
+        private final QualifiedName indexName;
+        private final List<IndexTarget.Raw> rawIndexTargets;
+        private final IndexAttributes attrs;
+        private final boolean ifNotExists;
+
+        public Raw(QualifiedName tableName,
+                   QualifiedName indexName,
+                   List<IndexTarget.Raw> rawIndexTargets,
+                   IndexAttributes attrs,
+                   boolean ifNotExists)
+        {
+            this.tableName = tableName;
+            this.indexName = indexName;
+            this.rawIndexTargets = rawIndexTargets;
+            this.attrs = attrs;
+            this.ifNotExists = ifNotExists;
+        }
+
+        public CreateIndexStatement prepare(ClientState state)
+        {
+            String keyspaceName = tableName.hasKeyspace()
+                                ? tableName.getKeyspace()
+                                : indexName.hasKeyspace() ? indexName.getKeyspace() : state.getKeyspace();
+
+            if (tableName.hasKeyspace() && !keyspaceName.equals(tableName.getKeyspace()))
+                throw ire("Keyspace name '%s' doesn't match table name '%s'", keyspaceName, tableName);
+
+            if (indexName.hasKeyspace() && !keyspaceName.equals(indexName.getKeyspace()))
+                throw ire("Keyspace name '%s' doesn't match index name '%s'", keyspaceName, tableName);
+
+            return new CreateIndexStatement(keyspaceName, tableName.getName(), indexName.getName(), rawIndexTargets, attrs, ifNotExists);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/schema/CreateKeyspaceStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateKeyspaceStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateKeyspaceStatement.java
new file mode 100644
index 0000000..5ea0859
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateKeyspaceStatement.java
@@ -0,0 +1,108 @@
+/*
+ * 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.Set;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.apache.cassandra.auth.*;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.statements.KeyspaceAttributes;
+import org.apache.cassandra.exceptions.AlreadyExistsException;
+import org.apache.cassandra.locator.LocalStrategy;
+import org.apache.cassandra.schema.KeyspaceMetadata;
+import org.apache.cassandra.schema.KeyspaceParams.Option;
+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 CreateKeyspaceStatement extends AlterSchemaStatement
+{
+    private final KeyspaceAttributes attrs;
+    private final boolean ifNotExists;
+
+    public CreateKeyspaceStatement(String keyspaceName, KeyspaceAttributes attrs, boolean ifNotExists)
+    {
+        super(keyspaceName);
+        this.attrs = attrs;
+        this.ifNotExists = ifNotExists;
+    }
+
+    public Keyspaces apply(Keyspaces schema)
+    {
+        attrs.validate();
+
+        if (!attrs.hasOption(Option.REPLICATION))
+            throw ire("Missing mandatory option '%s'", Option.REPLICATION);
+
+        if (schema.containsKeyspace(keyspaceName))
+        {
+            if (ifNotExists)
+                return schema;
+
+            throw new AlreadyExistsException(keyspaceName);
+        }
+
+        KeyspaceMetadata keyspace = KeyspaceMetadata.create(keyspaceName, attrs.asNewKeyspaceParams());
+
+        if (keyspace.params.replication.klass.equals(LocalStrategy.class))
+            throw ire("Unable to use given strategy class: LocalStrategy is reserved for internal use.");
+
+        keyspace.params.validate(keyspaceName);
+
+        return schema.withAddedOrUpdated(keyspace);
+    }
+
+    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
+    {
+        return new SchemaChange(Change.CREATED, keyspaceName);
+    }
+
+    public void authorize(ClientState client)
+    {
+        client.ensureAllKeyspacesPermission(Permission.CREATE);
+    }
+
+    @Override
+    Set<IResource> createdResources(KeyspacesDiff diff)
+    {
+        return ImmutableSet.of(DataResource.keyspace(keyspaceName), FunctionResource.keyspace(keyspaceName));
+    }
+
+    public static final class Raw extends CQLStatement.Raw
+    {
+        public final String keyspaceName;
+        private final KeyspaceAttributes attrs;
+        private final boolean ifNotExists;
+
+        public Raw(String keyspaceName, KeyspaceAttributes attrs, boolean ifNotExists)
+        {
+            this.keyspaceName = keyspaceName;
+            this.attrs = attrs;
+            this.ifNotExists = ifNotExists;
+        }
+
+        public CreateKeyspaceStatement prepare(ClientState state)
+        {
+            return new CreateKeyspaceStatement(keyspaceName, attrs, ifNotExists);
+        }
+    }
+}


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


Mime
View raw message