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-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org