You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by al...@apache.org on 2017/05/31 17:51:44 UTC
[05/11] cassandra git commit: 13426: work in progress
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