http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java deleted file mode 100644 index 260c8fd..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.cassandra.cql3.statements; - -import java.util.*; -import java.util.stream.Collectors; - -import com.google.common.collect.Iterables; - -import org.apache.cassandra.audit.AuditLogEntryType; -import org.apache.cassandra.auth.Permission; -import org.apache.cassandra.cql3.*; -import org.apache.cassandra.db.ColumnFamilyStore; -import org.apache.cassandra.db.Keyspace; -import org.apache.cassandra.db.marshal.AbstractType; -import org.apache.cassandra.db.marshal.CollectionType; -import org.apache.cassandra.db.view.View; -import org.apache.cassandra.exceptions.*; -import org.apache.cassandra.schema.ColumnMetadata; -import org.apache.cassandra.schema.DroppedColumn; -import org.apache.cassandra.schema.IndexMetadata; -import org.apache.cassandra.schema.Indexes; -import org.apache.cassandra.schema.MigrationManager; -import org.apache.cassandra.schema.Schema; -import org.apache.cassandra.schema.TableMetadata; -import org.apache.cassandra.schema.TableParams; -import org.apache.cassandra.schema.ViewMetadata; -import org.apache.cassandra.service.ClientState; -import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.transport.Event; - -public class AlterTableStatement extends SchemaAlteringStatement -{ - public enum Type - { - ADD, ALTER, DROP, OPTS, RENAME - } - - public final Type oType; - private final TableAttributes attrs; - private final Map<ColumnMetadata.Raw, ColumnMetadata.Raw> renames; - private final List<AlterTableStatementColumn> colNameList; - private final Long deleteTimestamp; - - public AlterTableStatement(CFName name, - Type type, - List<AlterTableStatementColumn> colDataList, - TableAttributes attrs, - Map<ColumnMetadata.Raw, ColumnMetadata.Raw> renames, - Long deleteTimestamp) - { - super(name); - this.oType = type; - this.colNameList = colDataList; - this.attrs = attrs; - this.renames = renames; - this.deleteTimestamp = deleteTimestamp; - } - - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException - { - state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.ALTER); - } - - public void validate(ClientState state) - { - // validated in announceMigration() - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException - { - TableMetadata current = Schema.instance.validateTable(keyspace(), columnFamily()); - if (current.isView()) - throw new InvalidRequestException("Cannot use ALTER TABLE on Materialized View"); - - if (current.isVirtual()) - throw new InvalidRequestException("Cannot alter virtual tables"); - - TableMetadata.Builder builder = current.unbuild(); - - ColumnIdentifier columnName = null; - ColumnMetadata def = null; - CQL3Type.Raw dataType = null; - boolean isStatic = false; - CQL3Type validator = null; - - List<ViewMetadata> viewUpdates = new ArrayList<>(); - Iterable<ViewMetadata> views = View.findAll(keyspace(), columnFamily()); - - switch (oType) - { - case ALTER: - throw new InvalidRequestException("Altering of types is not allowed"); - case ADD: - if (current.isDense()) - throw new InvalidRequestException("Cannot add new column to a COMPACT STORAGE table"); - - for (AlterTableStatementColumn colData : colNameList) - { - columnName = colData.getColumnName().getIdentifier(current); - def = builder.getColumn(columnName); - dataType = colData.getColumnType(); - assert dataType != null; - isStatic = colData.getStaticType(); - validator = dataType.prepare(keyspace()); - - - if (isStatic) - { - if (!current.isCompound()) - throw new InvalidRequestException("Static columns are not allowed in COMPACT STORAGE tables"); - if (current.clusteringColumns().isEmpty()) - throw new InvalidRequestException("Static columns are only useful (and thus allowed) if the table has at least one clustering column"); - } - - if (def != null) - { - switch (def.kind) - { - case PARTITION_KEY: - case CLUSTERING: - throw new InvalidRequestException(String.format("Invalid column name %s because it conflicts with a PRIMARY KEY part", columnName)); - default: - throw new InvalidRequestException(String.format("Invalid column name %s because it conflicts with an existing column", columnName)); - } - } - - // Cannot re-add a dropped counter column. See #7831. - if (current.isCounter() && current.getDroppedColumn(columnName.bytes) != null) - throw new InvalidRequestException(String.format("Cannot re-add previously dropped counter column %s", columnName)); - - AbstractType<?> type = validator.getType(); - if (type.isCollection() && type.isMultiCell()) - { - if (!current.isCompound()) - throw new InvalidRequestException("Cannot use non-frozen collections in COMPACT STORAGE tables"); - if (current.isSuper()) - throw new InvalidRequestException("Cannot use non-frozen collections with super column families"); - - // If there used to be a non-frozen collection column with the same name (that has been dropped), - // we could still have some data using the old type, and so we can't allow adding a collection - // with the same name unless the types are compatible (see #6276). - DroppedColumn dropped = current.droppedColumns.get(columnName.bytes); - if (dropped != null && dropped.column.type instanceof CollectionType - && dropped.column.type.isMultiCell() && !type.isCompatibleWith(dropped.column.type)) - { - String message = - String.format("Cannot add a collection with the name %s because a collection with the same name" - + " and a different type (%s) has already been used in the past", - columnName, - dropped.column.type.asCQL3Type()); - throw new InvalidRequestException(message); - } - } - - builder.addColumn(isStatic - ? ColumnMetadata.staticColumn(current, columnName.bytes, type) - : ColumnMetadata.regularColumn(current, columnName.bytes, type)); - - // Adding a column to a table which has an include all view requires the column to be added to the view - // as well - if (!isStatic) - for (ViewMetadata view : views) - if (view.includeAllColumns) - viewUpdates.add(view.withAddedRegularColumn(ColumnMetadata.regularColumn(view.metadata, columnName.bytes, type))); - - } - break; - case DROP: - if (!current.isCQLTable()) - throw new InvalidRequestException("Cannot drop columns from a non-CQL3 table"); - - for (AlterTableStatementColumn colData : colNameList) - { - columnName = colData.getColumnName().getIdentifier(current); - def = builder.getColumn(columnName); - - if (def == null) - throw new InvalidRequestException(String.format("Column %s was not found in table %s", columnName, columnFamily())); - - switch (def.kind) - { - case PARTITION_KEY: - case CLUSTERING: - throw new InvalidRequestException(String.format("Cannot drop PRIMARY KEY part %s", columnName)); - case REGULAR: - case STATIC: - builder.removeRegularOrStaticColumn(def.name); - builder.recordColumnDrop(def, deleteTimestamp == null ? queryState.getTimestamp() : deleteTimestamp); - break; - } - - // If the dropped column is required by any secondary indexes - // we reject the operation, as the indexes must be dropped first - Indexes allIndexes = current.indexes; - if (!allIndexes.isEmpty()) - { - ColumnFamilyStore store = Keyspace.openAndGetStore(current); - Set<IndexMetadata> dependentIndexes = store.indexManager.getDependentIndexes(def); - if (!dependentIndexes.isEmpty()) - { - throw new InvalidRequestException(String.format("Cannot drop column %s because it has " + - "dependent secondary indexes (%s)", - def, - dependentIndexes.stream() - .map(i -> i.name) - .collect(Collectors.joining(",")))); - } - } - - - if (!Iterables.isEmpty(views)) - throw new InvalidRequestException(String.format("Cannot drop column %s on base table %s with materialized views.", - columnName.toString(), - columnFamily())); - } - break; - case OPTS: - if (attrs == null) - throw new InvalidRequestException("ALTER TABLE WITH invoked, but no parameters found"); - attrs.validate(); - - TableParams params = attrs.asAlteredTableParams(current.params); - - if (!Iterables.isEmpty(views) && params.gcGraceSeconds == 0) - { - throw new InvalidRequestException("Cannot alter gc_grace_seconds of the base table of a " + - "materialized view to 0, since this value is used to TTL " + - "undelivered updates. Setting gc_grace_seconds too low might " + - "cause undelivered updates to expire " + - "before being replayed."); - } - - if (current.isCounter() && params.defaultTimeToLive > 0) - throw new InvalidRequestException("Cannot set default_time_to_live on a table with counters"); - - builder.params(params); - - break; - case RENAME: - for (Map.Entry<ColumnMetadata.Raw, ColumnMetadata.Raw> entry : renames.entrySet()) - { - ColumnIdentifier from = entry.getKey().getIdentifier(current); - ColumnIdentifier to = entry.getValue().getIdentifier(current); - - def = current.getColumn(from); - if (def == null) - throw new InvalidRequestException(String.format("Cannot rename unknown column %s in table %s", from, current.name)); - - if (current.getColumn(to) != null) - throw new InvalidRequestException(String.format("Cannot rename column %s to %s in table %s; another column of that name already exist", from, to, current.name)); - - if (!def.isPrimaryKeyColumn()) - throw new InvalidRequestException(String.format("Cannot rename non PRIMARY KEY part %s", from)); - - if (!current.indexes.isEmpty()) - { - ColumnFamilyStore store = Keyspace.openAndGetStore(current); - Set<IndexMetadata> dependentIndexes = store.indexManager.getDependentIndexes(def); - if (!dependentIndexes.isEmpty()) - throw new InvalidRequestException(String.format("Cannot rename column %s because it has " + - "dependent secondary indexes (%s)", - from, - dependentIndexes.stream() - .map(i -> i.name) - .collect(Collectors.joining(",")))); - } - - builder.renamePrimaryKeyColumn(from, to); - - // If the view includes a renamed column, it must be renamed in the view table and the definition. - for (ViewMetadata view : views) - { - if (!view.includes(from)) - continue; - - ColumnIdentifier viewFrom = entry.getKey().getIdentifier(view.metadata); - ColumnIdentifier viewTo = entry.getValue().getIdentifier(view.metadata); - viewUpdates.add(view.renamePrimaryKeyColumn(viewFrom, viewTo)); - } - } - break; - } - - MigrationManager.announceTableUpdate(builder.build(), viewUpdates, isLocalOnly); - return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily()); - } - - @Override - public String toString() - { - return String.format("AlterTableStatement(name=%s, type=%s)", - cfName, - oType); - } - - @Override - public AuditLogContext getAuditLogContext() - { - return new AuditLogContext(AuditLogEntryType.ALTER_TABLE, keyspace(), cfName.getColumnFamily()); - } -}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/statements/AlterTableStatementColumn.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatementColumn.java b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatementColumn.java deleted file mode 100644 index 97bf024..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatementColumn.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.cassandra.cql3.statements; - -import org.apache.cassandra.schema.ColumnMetadata; -import org.apache.cassandra.cql3.CQL3Type; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -/** - * Stores a column name and optionally type for an Alter Table statement definition. - * - * This is used by AlterTableStatement to store the added, altered or dropped columns. - */ -public class AlterTableStatementColumn -{ - private final CQL3Type.Raw dataType; - private final ColumnMetadata.Raw colName; - private final Boolean isStatic; - - public AlterTableStatementColumn(ColumnMetadata.Raw colName, CQL3Type.Raw dataType, boolean isStatic) - { - assert colName != null; - this.dataType = dataType; // will be null when dropping columns, and never null otherwise (for ADD and ALTER). - this.colName = colName; - this.isStatic = isStatic; - } - - public AlterTableStatementColumn(ColumnMetadata.Raw colName, CQL3Type.Raw dataType) - { - this(colName, dataType, false); - } - - public AlterTableStatementColumn(ColumnMetadata.Raw colName) - { - this(colName, null, false); - } - - public CQL3Type.Raw getColumnType() - { - return dataType; - } - - public ColumnMetadata.Raw getColumnName() - { - return colName; - } - - public Boolean getStaticType() - { - return isStatic; - } - - @Override - public String toString() - { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java deleted file mode 100644 index 3249af2..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.cassandra.cql3.statements; - -import java.util.*; - -import org.apache.cassandra.audit.AuditLogEntryType; -import org.apache.cassandra.auth.Permission; -import org.apache.cassandra.cql3.*; -import org.apache.cassandra.db.marshal.*; -import org.apache.cassandra.exceptions.*; -import org.apache.cassandra.schema.KeyspaceMetadata; -import org.apache.cassandra.schema.MigrationManager; -import org.apache.cassandra.schema.Schema; -import org.apache.cassandra.service.ClientState; -import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.transport.Event; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -public abstract class AlterTypeStatement extends SchemaAlteringStatement -{ - protected final UTName name; - - protected AlterTypeStatement(UTName name) - { - this.name = name; - } - - @Override - public void prepareKeyspace(ClientState state) throws InvalidRequestException - { - if (!name.hasKeyspace()) - name.setKeyspace(state.getKeyspace()); - - if (name.getKeyspace() == null) - throw new InvalidRequestException("You need to be logged in a keyspace or use a fully qualified user type name"); - } - - protected abstract UserType makeUpdatedType(UserType toUpdate, KeyspaceMetadata ksm) throws InvalidRequestException; - - public static AlterTypeStatement addition(UTName name, FieldIdentifier fieldName, CQL3Type.Raw type) - { - return new Add(name, fieldName, type); - } - - public static AlterTypeStatement alter(UTName name, FieldIdentifier fieldName, CQL3Type.Raw type) - { - throw new InvalidRequestException("Altering of types is not allowed"); - } - - public static AlterTypeStatement renames(UTName name, Map<FieldIdentifier, FieldIdentifier> renames) - { - return new Renames(name, renames); - } - - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException - { - state.hasKeyspaceAccess(keyspace(), Permission.ALTER); - } - - public void validate(ClientState state) throws RequestValidationException - { - // Validation is left to announceMigration as it's easier to do it while constructing the updated type. - // It doesn't really change anything anyway. - } - - @Override - public String keyspace() - { - return name.getKeyspace(); - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws InvalidRequestException, ConfigurationException - { - KeyspaceMetadata ksm = Schema.instance.getKeyspaceMetadata(name.getKeyspace()); - if (ksm == null) - throw new InvalidRequestException(String.format("Cannot alter type in unknown keyspace %s", name.getKeyspace())); - - UserType toUpdate = - ksm.types.get(name.getUserTypeName()) - .orElseThrow(() -> new InvalidRequestException(String.format("No user type named %s exists.", name))); - - UserType updated = makeUpdatedType(toUpdate, ksm); - - // Now, we need to announce the type update to basically change it for new tables using this type, - // but we also need to find all existing user types and CF using it and change them. - MigrationManager.announceTypeUpdate(updated, isLocalOnly); - - return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TYPE, keyspace(), name.getStringTypeName()); - } - - protected void checkTypeNotUsedByAggregate(KeyspaceMetadata ksm) - { - ksm.functions.udas().filter(aggregate -> aggregate.initialCondition() != null && aggregate.stateType().referencesUserType(name.getStringTypeName())) - .findAny() - .ifPresent((aggregate) -> { - throw new InvalidRequestException(String.format("Cannot alter user type %s as it is still used as an INITCOND by aggregate %s", name, aggregate)); - }); - } - - private static class Add extends AlterTypeStatement - { - private final FieldIdentifier fieldName; - private final CQL3Type.Raw type; - - public Add(UTName name, FieldIdentifier fieldName, CQL3Type.Raw type) - { - super(name); - this.fieldName = fieldName; - this.type = type; - } - - protected UserType makeUpdatedType(UserType toUpdate, KeyspaceMetadata ksm) throws InvalidRequestException - { - if (toUpdate.fieldPosition(fieldName) >= 0) - throw new InvalidRequestException(String.format("Cannot add new field %s to type %s: a field of the same name already exists", fieldName, name)); - - List<FieldIdentifier> newNames = new ArrayList<>(toUpdate.size() + 1); - newNames.addAll(toUpdate.fieldNames()); - newNames.add(fieldName); - - AbstractType<?> addType = type.prepare(keyspace()).getType(); - if (addType.referencesUserType(toUpdate.getNameAsString())) - throw new InvalidRequestException(String.format("Cannot add new field %s of type %s to type %s as this would create a circular reference", fieldName, type, name)); - - List<AbstractType<?>> newTypes = new ArrayList<>(toUpdate.size() + 1); - newTypes.addAll(toUpdate.fieldTypes()); - newTypes.add(addType); - - return new UserType(toUpdate.keyspace, toUpdate.name, newNames, newTypes, toUpdate.isMultiCell()); - } - } - - private static class Renames extends AlterTypeStatement - { - private final Map<FieldIdentifier, FieldIdentifier> renames; - - public Renames(UTName name, Map<FieldIdentifier, FieldIdentifier> renames) - { - super(name); - this.renames = renames; - } - - protected UserType makeUpdatedType(UserType toUpdate, KeyspaceMetadata ksm) throws InvalidRequestException - { - checkTypeNotUsedByAggregate(ksm); - - List<FieldIdentifier> newNames = new ArrayList<>(toUpdate.fieldNames()); - List<AbstractType<?>> newTypes = new ArrayList<>(toUpdate.fieldTypes()); - - for (Map.Entry<FieldIdentifier, FieldIdentifier> entry : renames.entrySet()) - { - FieldIdentifier from = entry.getKey(); - FieldIdentifier to = entry.getValue(); - int idx = toUpdate.fieldPosition(from); - if (idx < 0) - throw new InvalidRequestException(String.format("Unknown field %s in type %s", from, name)); - newNames.set(idx, to); - } - - UserType updated = new UserType(toUpdate.keyspace, toUpdate.name, newNames, newTypes, toUpdate.isMultiCell()); - CreateTypeStatement.checkForDuplicateNames(updated); - return updated; - } - - } - - @Override - public String toString() - { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); - } - - @Override - public AuditLogContext getAuditLogContext() - { - return new AuditLogContext(AuditLogEntryType.ALTER_TYPE, keyspace(), name.getStringTypeName()); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/statements/AlterViewStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterViewStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterViewStatement.java deleted file mode 100644 index 938908c..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/AlterViewStatement.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.cassandra.cql3.statements; - -import org.apache.cassandra.audit.AuditLogEntryType; -import org.apache.cassandra.auth.Permission; -import org.apache.cassandra.cql3.CFName; -import org.apache.cassandra.db.view.View; -import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.exceptions.RequestValidationException; -import org.apache.cassandra.exceptions.UnauthorizedException; -import org.apache.cassandra.schema.MigrationManager; -import org.apache.cassandra.schema.Schema; -import org.apache.cassandra.schema.TableMetadata; -import org.apache.cassandra.schema.TableMetadataRef; -import org.apache.cassandra.schema.TableParams; -import org.apache.cassandra.schema.ViewMetadata; -import org.apache.cassandra.service.ClientState; -import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.transport.Event; - -public class AlterViewStatement extends SchemaAlteringStatement -{ - private final TableAttributes attrs; - - public AlterViewStatement(CFName name, TableAttributes attrs) - { - super(name); - this.attrs = attrs; - } - - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException - { - TableMetadataRef baseTable = View.findBaseTable(keyspace(), columnFamily()); - if (baseTable != null) - state.hasColumnFamilyAccess(keyspace(), baseTable.name, Permission.ALTER); - } - - public void validate(ClientState state) - { - // validated in announceMigration() - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException - { - TableMetadata meta = Schema.instance.validateTable(keyspace(), columnFamily()); - if (!meta.isView()) - throw new InvalidRequestException("Cannot use ALTER MATERIALIZED VIEW on Table"); - - ViewMetadata current = Schema.instance.getView(keyspace(), columnFamily()); - - if (attrs == null) - throw new InvalidRequestException("ALTER MATERIALIZED VIEW WITH invoked, but no parameters found"); - - attrs.validate(); - - TableParams params = attrs.asAlteredTableParams(current.metadata.params); - if (params.gcGraceSeconds == 0) - { - throw new InvalidRequestException("Cannot alter gc_grace_seconds of a materialized view to 0, since this " + - "value is used to TTL undelivered updates. Setting gc_grace_seconds too " + - "low might cause undelivered updates to expire before being replayed."); - } - - if (params.defaultTimeToLive > 0) - { - throw new InvalidRequestException("Cannot set or alter default_time_to_live for a materialized view. " + - "Data in a materialized view always expire at the same time than " + - "the corresponding data in the parent table."); - } - - ViewMetadata updated = current.copy(current.metadata.unbuild().params(params).build()); - - MigrationManager.announceViewUpdate(updated, isLocalOnly); - return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily()); - } - - public String toString() - { - return String.format("AlterViewStatement(name=%s)", cfName); - } - - @Override - public AuditLogContext getAuditLogContext() - { - return new AuditLogContext(AuditLogEntryType.ALTER_VIEW, keyspace(), cfName.getColumnFamily()); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java b/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java index 0283009..a8cbaa7 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AuthenticationStatement.java @@ -28,17 +28,11 @@ import org.apache.cassandra.service.ClientState; import org.apache.cassandra.service.QueryState; import org.apache.cassandra.transport.messages.ResultMessage; -public abstract class AuthenticationStatement extends ParsedStatement implements CQLStatement +public abstract class AuthenticationStatement extends CQLStatement.Raw implements CQLStatement { - @Override - public Prepared prepare() + public AuthenticationStatement prepare(ClientState state) { - return new Prepared(this); - } - - public int getBoundTerms() - { - return 0; + return this; } public ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime) @@ -49,9 +43,9 @@ public abstract class AuthenticationStatement extends ParsedStatement implements public abstract ResultMessage execute(ClientState state) throws RequestExecutionException, RequestValidationException; - public ResultMessage executeInternal(QueryState state, QueryOptions options) + public ResultMessage executeLocally(QueryState state, QueryOptions options) { - // executeInternal is for local query only, thus altering users doesn't make sense and is not supported + // executeLocally is for local query only, thus altering users doesn't make sense and is not supported throw new UnsupportedOperationException(); } @@ -59,7 +53,7 @@ public abstract class AuthenticationStatement extends ParsedStatement implements { try { - state.ensureHasPermission(required, resource); + state.ensurePermission(required, resource); } catch (UnauthorizedException e) { http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/statements/AuthorizationStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/AuthorizationStatement.java b/src/java/org/apache/cassandra/cql3/statements/AuthorizationStatement.java index 8c6f93b..46285c6 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AuthorizationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AuthorizationStatement.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.cql3.statements; - import org.apache.cassandra.auth.DataResource; import org.apache.cassandra.auth.IResource; import org.apache.cassandra.cql3.CQLStatement; @@ -31,17 +30,11 @@ import org.apache.cassandra.transport.messages.ResultMessage; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -public abstract class AuthorizationStatement extends ParsedStatement implements CQLStatement +public abstract class AuthorizationStatement extends CQLStatement.Raw implements CQLStatement { - @Override - public Prepared prepare() - { - return new Prepared(this); - } - - public int getBoundTerms() + public AuthorizationStatement prepare(ClientState state) { - return 0; + return this; } public ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime) @@ -52,9 +45,9 @@ public abstract class AuthorizationStatement extends ParsedStatement implements public abstract ResultMessage execute(ClientState state) throws RequestValidationException, RequestExecutionException; - public ResultMessage executeInternal(QueryState state, QueryOptions options) + public ResultMessage executeLocally(QueryState state, QueryOptions options) { - // executeInternal is for local query only, thus altering permission doesn't make sense and is not supported + // executeLocally is for local query only, thus altering permission doesn't make sense and is not supported throw new UnsupportedOperationException(); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java index a71c799..9ed150c 100644 --- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java @@ -27,6 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; +import org.apache.cassandra.audit.AuditLogContext; import org.apache.cassandra.audit.AuditLogEntryType; import org.apache.cassandra.schema.TableId; import org.apache.cassandra.schema.TableMetadata; @@ -45,6 +46,8 @@ import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.NoSpamLogger; import org.apache.cassandra.utils.Pair; +import static java.util.function.Predicate.isEqual; + import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse; /** @@ -57,8 +60,8 @@ public class BatchStatement implements CQLStatement LOGGED, UNLOGGED, COUNTER } - private final int boundTerms; public final Type type; + private final VariableSpecifications bindVariables; private final List<ModificationStatement> statements; // Columns modified for each table (keyed by the table ID) @@ -93,10 +96,10 @@ public class BatchStatement implements CQLStatement * @param statements the list of statements in the batch * @param attrs additional attributes for statement (CL, timestamp, timeToLive) */ - public BatchStatement(int boundTerms, Type type, List<ModificationStatement> statements, Attributes attrs) + public BatchStatement(Type type, VariableSpecifications bindVariables, List<ModificationStatement> statements, Attributes attrs) { - this.boundTerms = boundTerms; this.type = type; + this.bindVariables = bindVariables; this.statements = statements; this.attrs = attrs; @@ -128,6 +131,26 @@ public class BatchStatement implements CQLStatement this.updatesVirtualTables = updatesVirtualTables; } + @Override + public List<ColumnSpecification> getBindVariables() + { + return bindVariables.getBindVariables(); + } + + @Override + public short[] getPartitionKeyBindVariableIndexes() + { + boolean affectsMultipleTables = + !statements.isEmpty() && statements.stream().map(s -> s.metadata().id).allMatch(isEqual(statements.get(0).metadata().id)); + + // Use the TableMetadata of the first statement for partition key bind indexes. If the statements affect + // multiple tables, we won't send partition key bind indexes. + return (affectsMultipleTables || statements.isEmpty()) + ? null + : bindVariables.getPartitionKeyBindVariableIndexes(statements.get(0).metadata()); + } + + @Override public Iterable<org.apache.cassandra.cql3.functions.Function> getFunctions() { List<org.apache.cassandra.cql3.functions.Function> functions = new ArrayList<>(); @@ -136,15 +159,10 @@ public class BatchStatement implements CQLStatement return functions; } - public int getBoundTerms() - { - return boundTerms; - } - - public void checkAccess(ClientState state) throws InvalidRequestException, UnauthorizedException + public void authorize(ClientState state) throws InvalidRequestException, UnauthorizedException { for (ModificationStatement statement : statements) - statement.checkAccess(state); + statement.authorize(state); } // Validates a prepared batch statement without validating its nested statements. @@ -506,7 +524,7 @@ public class BatchStatement implements CQLStatement return hasConditions; } - public ResultMessage executeInternal(QueryState queryState, QueryOptions options) throws RequestValidationException, RequestExecutionException + public ResultMessage executeLocally(QueryState queryState, QueryOptions options) throws RequestValidationException, RequestExecutionException { BatchQueryOptions batchOptions = BatchQueryOptions.withoutPerStatementVariables(options); @@ -544,7 +562,7 @@ public class BatchStatement implements CQLStatement return String.format("BatchStatement(type=%s, statements=%s)", type, statements); } - public static class Parsed extends CFStatement + public static class Parsed extends QualifiedStatement { private final Type type; private final Attributes.Raw attrs; @@ -559,48 +577,24 @@ public class BatchStatement implements CQLStatement } @Override - public void prepareKeyspace(ClientState state) throws InvalidRequestException + public void setKeyspace(ClientState state) throws InvalidRequestException { for (ModificationStatement.Parsed statement : parsedStatements) - statement.prepareKeyspace(state); + statement.setKeyspace(state); } - public ParsedStatement.Prepared prepare() throws InvalidRequestException + public BatchStatement prepare(ClientState state) { - VariableSpecifications boundNames = getBoundVariables(); - - String firstKS = null; - String firstCF = null; - boolean haveMultipleCFs = false; - List<ModificationStatement> statements = new ArrayList<>(parsedStatements.size()); - for (ModificationStatement.Parsed parsed : parsedStatements) - { - if (firstKS == null) - { - firstKS = parsed.keyspace(); - firstCF = parsed.columnFamily(); - } - else if (!haveMultipleCFs) - { - haveMultipleCFs = !firstKS.equals(parsed.keyspace()) || !firstCF.equals(parsed.columnFamily()); - } - - statements.add(parsed.prepare(boundNames)); - } + parsedStatements.forEach(s -> statements.add(s.prepare(bindVariables))); Attributes prepAttrs = attrs.prepare("[batch]", "[batch]"); - prepAttrs.collectMarkerSpecification(boundNames); + prepAttrs.collectMarkerSpecification(bindVariables); - BatchStatement batchStatement = new BatchStatement(boundNames.size(), type, statements, prepAttrs); + BatchStatement batchStatement = new BatchStatement(type, bindVariables, statements, prepAttrs); batchStatement.validate(); - // Use the TableMetadata of the first statement for partition key bind indexes. If the statements affect - // multiple tables, we won't send partition key bind indexes. - short[] partitionKeyBindIndexes = (haveMultipleCFs || batchStatement.statements.isEmpty())? null - : boundNames.getPartitionKeyBindIndexes(batchStatement.statements.get(0).metadata()); - - return new ParsedStatement.Prepared(batchStatement, boundNames, partitionKeyBindIndexes); + return batchStatement; } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/statements/CFProperties.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CFProperties.java b/src/java/org/apache/cassandra/cql3/statements/CFProperties.java deleted file mode 100644 index 3f40c0d..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/CFProperties.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.cassandra.cql3.statements; - -import java.util.LinkedHashMap; -import java.util.Map; - -import org.apache.cassandra.cql3.ColumnIdentifier; -import org.apache.cassandra.db.marshal.AbstractType; -import org.apache.cassandra.db.marshal.ReversedType; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -public class CFProperties -{ - public final TableAttributes properties = new TableAttributes(); - final Map<ColumnIdentifier, Boolean> definedOrdering = new LinkedHashMap<>(); // Insertion ordering is important - boolean useCompactStorage = false; - - public void validate() - { - properties.validate(); - } - - public void setOrdering(ColumnIdentifier alias, boolean reversed) - { - definedOrdering.put(alias, reversed); - } - - public void setCompactStorage() - { - useCompactStorage = true; - } - - public AbstractType getReversableType(ColumnIdentifier targetIdentifier, AbstractType<?> type) - { - if (!definedOrdering.containsKey(targetIdentifier)) - { - return type; - } - return definedOrdering.get(targetIdentifier) ? ReversedType.getInstance(type) : type; - } - - @Override - public String toString() - { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/statements/CFStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CFStatement.java b/src/java/org/apache/cassandra/cql3/statements/CFStatement.java deleted file mode 100644 index 7cc8a99..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/CFStatement.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.cassandra.cql3.statements; - -import org.apache.cassandra.cql3.CFName; -import org.apache.cassandra.service.ClientState; -import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -/** - * Abstract class for statements that apply on a given column family. - */ -public abstract class CFStatement extends ParsedStatement -{ - protected final CFName cfName; - - protected CFStatement(CFName cfName) - { - this.cfName = cfName; - } - - public void prepareKeyspace(ClientState state) throws InvalidRequestException - { - if (!cfName.hasKeyspace()) - { - // XXX: We explicitly only want to call state.getKeyspace() in this case, as we don't want to throw - // if not logged in any keyspace but a keyspace is explicitly set on the statement. So don't move - // the call outside the 'if' or replace the method by 'prepareKeyspace(state.getKeyspace())' - cfName.setKeyspace(state.getKeyspace(), true); - } - } - - // Only for internal calls, use the version with ClientState for user queries. In particular, the - // version with ClientState throws an exception if the statement does not have keyspace set *and* - // ClientState has no keyspace. - public void prepareKeyspace(String keyspace) - { - if (!cfName.hasKeyspace()) - cfName.setKeyspace(keyspace, true); - } - - public String keyspace() - { - assert cfName.hasKeyspace() : "The statement hasn't been prepared correctly"; - return cfName.getKeyspace(); - } - - public String columnFamily() - { - return cfName.getColumnFamily(); - } - - @Override - public String toString() - { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java deleted file mode 100644 index e428087..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.cassandra.cql3.statements; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Objects; -import java.util.List; - -import org.apache.cassandra.audit.AuditLogEntryType; -import org.apache.cassandra.auth.*; -import org.apache.cassandra.config.DatabaseDescriptor; -import org.apache.cassandra.schema.KeyspaceMetadata; -import org.apache.cassandra.schema.Schema; -import org.apache.cassandra.cql3.*; -import org.apache.cassandra.cql3.functions.*; -import org.apache.cassandra.db.marshal.AbstractType; -import org.apache.cassandra.exceptions.*; -import org.apache.cassandra.serializers.MarshalException; -import org.apache.cassandra.service.ClientState; -import org.apache.cassandra.schema.MigrationManager; -import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.transport.Event; -import org.apache.cassandra.transport.ProtocolVersion; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -/** - * A {@code CREATE AGGREGATE} statement parsed from a CQL query. - */ -public final class CreateAggregateStatement extends SchemaAlteringStatement -{ - private final boolean orReplace; - private final boolean ifNotExists; - private FunctionName functionName; - private FunctionName stateFunc; - private FunctionName finalFunc; - private final CQL3Type.Raw stateTypeRaw; - - private final List<CQL3Type.Raw> argRawTypes; - private final Term.Raw ival; - - private List<AbstractType<?>> argTypes; - private AbstractType<?> returnType; - private ScalarFunction stateFunction; - private ScalarFunction finalFunction; - private ByteBuffer initcond; - - public CreateAggregateStatement(FunctionName functionName, - List<CQL3Type.Raw> argRawTypes, - String stateFunc, - CQL3Type.Raw stateType, - String finalFunc, - Term.Raw ival, - boolean orReplace, - boolean ifNotExists) - { - this.functionName = functionName; - this.argRawTypes = argRawTypes; - this.stateFunc = new FunctionName(functionName.keyspace, stateFunc); - this.finalFunc = finalFunc != null ? new FunctionName(functionName.keyspace, finalFunc) : null; - this.stateTypeRaw = stateType; - this.ival = ival; - this.orReplace = orReplace; - this.ifNotExists = ifNotExists; - } - - public Prepared prepare() - { - argTypes = new ArrayList<>(argRawTypes.size()); - for (CQL3Type.Raw rawType : argRawTypes) - argTypes.add(prepareType("arguments", rawType)); - - AbstractType<?> stateType = prepareType("state type", stateTypeRaw); - - List<AbstractType<?>> stateArgs = stateArguments(stateType, argTypes); - - Function f = Schema.instance.findFunction(stateFunc, stateArgs).orElse(null); - if (!(f instanceof ScalarFunction)) - throw new InvalidRequestException("State function " + stateFuncSig(stateFunc, stateTypeRaw, argRawTypes) + " does not exist or is not a scalar function"); - stateFunction = (ScalarFunction)f; - - AbstractType<?> stateReturnType = stateFunction.returnType(); - if (!stateReturnType.equals(stateType)) - throw new InvalidRequestException("State function " + stateFuncSig(stateFunction.name(), stateTypeRaw, argRawTypes) + " return type must be the same as the first argument type - check STYPE, argument and return types"); - - if (finalFunc != null) - { - List<AbstractType<?>> finalArgs = Collections.<AbstractType<?>>singletonList(stateType); - f = Schema.instance.findFunction(finalFunc, finalArgs).orElse(null); - if (!(f instanceof ScalarFunction)) - throw new InvalidRequestException("Final function " + finalFunc + '(' + stateTypeRaw + ") does not exist or is not a scalar function"); - finalFunction = (ScalarFunction) f; - returnType = finalFunction.returnType(); - } - else - { - returnType = stateReturnType; - } - - if (ival != null) - { - initcond = Terms.asBytes(functionName.keyspace, ival.toString(), stateType); - - if (initcond != null) - { - try - { - stateType.validate(initcond); - } - catch (MarshalException e) - { - throw new InvalidRequestException(String.format("Invalid value for INITCOND of type %s%s", stateType.asCQL3Type(), - e.getMessage() == null ? "" : String.format(" (%s)", e.getMessage()))); - } - } - - // Sanity check that converts the initcond to a CQL literal and parse it back to avoid getting in CASSANDRA-11064. - String initcondAsCql = stateType.asCQL3Type().toCQLLiteral(initcond, ProtocolVersion.CURRENT); - assert Objects.equals(initcond, Terms.asBytes(functionName.keyspace, initcondAsCql, stateType)); - - if (Constants.NULL_LITERAL != ival && UDHelper.isNullOrEmpty(stateType, initcond)) - throw new InvalidRequestException("INITCOND must not be empty for all types except TEXT, ASCII, BLOB"); - } - - return super.prepare(); - } - - private AbstractType<?> prepareType(String typeName, CQL3Type.Raw rawType) - { - if (rawType.isFrozen()) - throw new InvalidRequestException(String.format("The function %s should not be frozen; remove the frozen<> modifier", typeName)); - - // UDT are not supported non frozen but we do not allow the frozen keyword for argument. So for the moment we - // freeze them here - if (!rawType.canBeNonFrozen()) - rawType.freeze(); - - AbstractType<?> type = rawType.prepare(functionName.keyspace).getType(); - return type; - } - - public void prepareKeyspace(ClientState state) throws InvalidRequestException - { - if (!functionName.hasKeyspace() && state.getRawKeyspace() != null) - functionName = new FunctionName(state.getKeyspace(), functionName.name); - - if (!functionName.hasKeyspace()) - throw new InvalidRequestException("Functions must be fully qualified with a keyspace name if a keyspace is not set for the session"); - - Schema.validateKeyspaceNotSystem(functionName.keyspace); - - stateFunc = new FunctionName(functionName.keyspace, stateFunc.name); - if (finalFunc != null) - finalFunc = new FunctionName(functionName.keyspace, finalFunc.name); - } - - protected void grantPermissionsToCreator(QueryState state) - { - try - { - IResource resource = FunctionResource.function(functionName.keyspace, functionName.name, argTypes); - DatabaseDescriptor.getAuthorizer().grant(AuthenticatedUser.SYSTEM_USER, - resource.applicablePermissions(), - resource, - RoleResource.role(state.getClientState().getUser().getName())); - } - catch (RequestExecutionException e) - { - throw new RuntimeException(e); - } - } - - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException - { - if (Schema.instance.findFunction(functionName, argTypes).isPresent() && orReplace) - state.ensureHasPermission(Permission.ALTER, FunctionResource.function(functionName.keyspace, - functionName.name, - argTypes)); - else - state.ensureHasPermission(Permission.CREATE, FunctionResource.keyspace(functionName.keyspace)); - - state.ensureHasPermission(Permission.EXECUTE, stateFunction); - - if (finalFunction != null) - state.ensureHasPermission(Permission.EXECUTE, finalFunction); - } - - public void validate(ClientState state) throws InvalidRequestException - { - if (ifNotExists && orReplace) - throw new InvalidRequestException("Cannot use both 'OR REPLACE' and 'IF NOT EXISTS' directives"); - - - KeyspaceMetadata ksm = Schema.instance.getKeyspaceMetadata(functionName.keyspace); - if (null == ksm) - throw new InvalidRequestException(String.format("Cannot add aggregate '%s' to non existing keyspace '%s'.", functionName.name, functionName.keyspace)); - if (ksm.isVirtual()) - throw new InvalidRequestException("Cannot create aggregates in virtual keyspaces"); - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException - { - Function old = Schema.instance.findFunction(functionName, argTypes).orElse(null); - boolean replaced = old != null; - if (replaced) - { - if (ifNotExists) - return null; - if (!orReplace) - throw new InvalidRequestException(String.format("Function %s already exists", old)); - if (!(old instanceof AggregateFunction)) - throw new InvalidRequestException(String.format("Aggregate %s can only replace an aggregate", old)); - - // Means we're replacing the function. We still need to validate that 1) it's not a native function and 2) that the return type - // matches (or that could break existing code badly) - if (old.isNative()) - throw new InvalidRequestException(String.format("Cannot replace native aggregate %s", old)); - if (!old.returnType().isValueCompatibleWith(returnType)) - throw new InvalidRequestException(String.format("Cannot replace aggregate %s, the new return type %s is not compatible with the return type %s of existing function", - functionName, returnType.asCQL3Type(), old.returnType().asCQL3Type())); - } - - if (!stateFunction.isCalledOnNullInput() && initcond == null) - throw new InvalidRequestException(String.format("Cannot create aggregate %s without INITCOND because state function %s does not accept 'null' arguments", functionName, stateFunc)); - - UDAggregate udAggregate = new UDAggregate(functionName, argTypes, returnType, stateFunction, finalFunction, initcond); - - MigrationManager.announceNewAggregate(udAggregate, isLocalOnly); - - return new Event.SchemaChange(replaced ? Event.SchemaChange.Change.UPDATED : Event.SchemaChange.Change.CREATED, - Event.SchemaChange.Target.AGGREGATE, - udAggregate.name().keyspace, udAggregate.name().name, AbstractType.asCQLTypeStringList(udAggregate.argTypes())); - } - - private static String stateFuncSig(FunctionName stateFuncName, CQL3Type.Raw stateTypeRaw, List<CQL3Type.Raw> argRawTypes) - { - StringBuilder sb = new StringBuilder(); - sb.append(stateFuncName.toString()).append('(').append(stateTypeRaw); - for (CQL3Type.Raw argRawType : argRawTypes) - sb.append(", ").append(argRawType); - sb.append(')'); - return sb.toString(); - } - - private static List<AbstractType<?>> stateArguments(AbstractType<?> stateType, List<AbstractType<?>> argTypes) - { - List<AbstractType<?>> r = new ArrayList<>(argTypes.size() + 1); - r.add(stateType); - r.addAll(argTypes); - return r; - } - - @Override - public String toString() - { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); - } - - @Override - public AuditLogContext getAuditLogContext() - { - return new AuditLogContext(AuditLogEntryType.CREATE_AGGREGATE, keyspace(), functionName.name); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java deleted file mode 100644 index c380991..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.cassandra.cql3.statements; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -import org.apache.cassandra.audit.AuditLogEntryType; -import org.apache.cassandra.auth.*; -import org.apache.cassandra.config.DatabaseDescriptor; -import org.apache.cassandra.schema.KeyspaceMetadata; -import org.apache.cassandra.schema.Schema; -import org.apache.cassandra.cql3.CQL3Type; -import org.apache.cassandra.cql3.ColumnIdentifier; -import org.apache.cassandra.cql3.functions.*; -import org.apache.cassandra.db.marshal.AbstractType; -import org.apache.cassandra.exceptions.*; -import org.apache.cassandra.schema.Functions; -import org.apache.cassandra.service.ClientState; -import org.apache.cassandra.schema.MigrationManager; -import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.transport.Event; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -/** - * A {@code CREATE FUNCTION} statement parsed from a CQL query. - */ -public final class CreateFunctionStatement extends SchemaAlteringStatement -{ - private final boolean orReplace; - private final boolean ifNotExists; - private FunctionName functionName; - private final String language; - private final String body; - - private final List<ColumnIdentifier> argNames; - private final List<CQL3Type.Raw> argRawTypes; - private final CQL3Type.Raw rawReturnType; - private final boolean calledOnNullInput; - - private List<AbstractType<?>> argTypes; - private AbstractType<?> returnType; - - public CreateFunctionStatement(FunctionName functionName, - String language, - String body, - List<ColumnIdentifier> argNames, - List<CQL3Type.Raw> argRawTypes, - CQL3Type.Raw rawReturnType, - boolean calledOnNullInput, - boolean orReplace, - boolean ifNotExists) - { - this.functionName = functionName; - this.language = language; - this.body = body; - this.argNames = argNames; - this.argRawTypes = argRawTypes; - this.rawReturnType = rawReturnType; - this.calledOnNullInput = calledOnNullInput; - this.orReplace = orReplace; - this.ifNotExists = ifNotExists; - } - - public Prepared prepare() throws InvalidRequestException - { - if (new HashSet<>(argNames).size() != argNames.size()) - throw new InvalidRequestException(String.format("duplicate argument names for given function %s with argument names %s", - functionName, argNames)); - - argTypes = new ArrayList<>(argRawTypes.size()); - for (CQL3Type.Raw rawType : argRawTypes) - argTypes.add(prepareType("arguments", rawType)); - - returnType = prepareType("return type", rawReturnType); - return super.prepare(); - } - - public void prepareKeyspace(ClientState state) throws InvalidRequestException - { - if (!functionName.hasKeyspace() && state.getRawKeyspace() != null) - functionName = new FunctionName(state.getRawKeyspace(), functionName.name); - - if (!functionName.hasKeyspace()) - throw new InvalidRequestException("Functions must be fully qualified with a keyspace name if a keyspace is not set for the session"); - - Schema.validateKeyspaceNotSystem(functionName.keyspace); - } - - protected void grantPermissionsToCreator(QueryState state) - { - try - { - IResource resource = FunctionResource.function(functionName.keyspace, functionName.name, argTypes); - DatabaseDescriptor.getAuthorizer().grant(AuthenticatedUser.SYSTEM_USER, - resource.applicablePermissions(), - resource, - RoleResource.role(state.getClientState().getUser().getName())); - } - catch (RequestExecutionException e) - { - throw new RuntimeException(e); - } - } - - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException - { - if (Schema.instance.findFunction(functionName, argTypes).isPresent() && orReplace) - state.ensureHasPermission(Permission.ALTER, FunctionResource.function(functionName.keyspace, - functionName.name, - argTypes)); - else - state.ensureHasPermission(Permission.CREATE, FunctionResource.keyspace(functionName.keyspace)); - } - - public void validate(ClientState state) throws InvalidRequestException - { - UDFunction.assertUdfsEnabled(language); - - if (ifNotExists && orReplace) - throw new InvalidRequestException("Cannot use both 'OR REPLACE' and 'IF NOT EXISTS' directives"); - - KeyspaceMetadata ksm = Schema.instance.getKeyspaceMetadata(functionName.keyspace); - if (null == ksm) - throw new InvalidRequestException(String.format("Cannot add function '%s' to non existing keyspace '%s'.", functionName.name, functionName.keyspace)); - if (ksm.isVirtual()) - throw new InvalidRequestException("Cannot create functions in virtual keyspaces"); - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException - { - Function old = Schema.instance.findFunction(functionName, argTypes).orElse(null); - boolean replaced = old != null; - if (replaced) - { - if (ifNotExists) - return null; - if (!orReplace) - throw new InvalidRequestException(String.format("Function %s already exists", old)); - if (!(old instanceof ScalarFunction)) - throw new InvalidRequestException(String.format("Function %s can only replace a function", old)); - if (calledOnNullInput != ((ScalarFunction) old).isCalledOnNullInput()) - throw new InvalidRequestException(String.format("Function %s can only be replaced with %s", old, - calledOnNullInput ? "CALLED ON NULL INPUT" : "RETURNS NULL ON NULL INPUT")); - - if (!Functions.typesMatch(old.returnType(), returnType)) - throw new InvalidRequestException(String.format("Cannot replace function %s, the new return type %s is not compatible with the return type %s of existing function", - functionName, returnType.asCQL3Type(), old.returnType().asCQL3Type())); - } - - UDFunction udFunction = UDFunction.create(functionName, argNames, argTypes, returnType, calledOnNullInput, language, body); - - MigrationManager.announceNewFunction(udFunction, isLocalOnly); - - return new Event.SchemaChange(replaced ? Event.SchemaChange.Change.UPDATED : Event.SchemaChange.Change.CREATED, - Event.SchemaChange.Target.FUNCTION, - udFunction.name().keyspace, udFunction.name().name, AbstractType.asCQLTypeStringList(udFunction.argTypes())); - } - - private AbstractType<?> prepareType(String typeName, CQL3Type.Raw rawType) - { - if (rawType.isFrozen()) - throw new InvalidRequestException(String.format("The function %s should not be frozen; remove the frozen<> modifier", typeName)); - - // UDT are not supported non frozen but we do not allow the frozen keyword for argument. So for the moment we - // freeze them here - if (!rawType.canBeNonFrozen()) - rawType.freeze(); - - AbstractType<?> type = rawType.prepare(functionName.keyspace).getType(); - return type; - } - - @Override - public String toString() - { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); - } - - @Override - public AuditLogContext getAuditLogContext() - { - return new AuditLogContext(AuditLogEntryType.CREATE_FUNCTION, functionName.keyspace, functionName.name); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java deleted file mode 100644 index 778c4a3..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.cassandra.cql3.statements; - -import java.util.*; - -import com.google.common.base.Optional; -import com.google.common.base.Strings; -import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.apache.cassandra.audit.AuditLogEntryType; -import org.apache.cassandra.auth.Permission; -import org.apache.cassandra.cql3.CFName; -import org.apache.cassandra.cql3.ColumnIdentifier; -import org.apache.cassandra.cql3.IndexName; -import org.apache.cassandra.db.marshal.MapType; -import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.exceptions.RequestValidationException; -import org.apache.cassandra.exceptions.UnauthorizedException; -import org.apache.cassandra.schema.ColumnMetadata; -import org.apache.cassandra.schema.IndexMetadata; -import org.apache.cassandra.schema.Indexes; -import org.apache.cassandra.schema.MigrationManager; -import org.apache.cassandra.schema.Schema; -import org.apache.cassandra.schema.TableMetadata; -import org.apache.cassandra.service.ClientState; -import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.transport.Event; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse; -import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest; - -/** A <code>CREATE INDEX</code> statement parsed from a CQL query. */ -public class CreateIndexStatement extends SchemaAlteringStatement -{ - private static final Logger logger = LoggerFactory.getLogger(CreateIndexStatement.class); - - private final String indexName; - private final List<IndexTarget.Raw> rawTargets; - private final IndexPropDefs properties; - private final boolean ifNotExists; - - public CreateIndexStatement(CFName name, - IndexName indexName, - List<IndexTarget.Raw> targets, - IndexPropDefs properties, - boolean ifNotExists) - { - super(name); - this.indexName = indexName.getIdx(); - this.rawTargets = targets; - this.properties = properties; - this.ifNotExists = ifNotExists; - } - - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException - { - state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.ALTER); - } - - public void validate(ClientState state) throws RequestValidationException - { - TableMetadata table = Schema.instance.validateTable(keyspace(), columnFamily()); - - if (table.isVirtual()) - throw new InvalidRequestException("Secondary indexes are not supported on virtual tables"); - - if (table.isCounter()) - throw new InvalidRequestException("Secondary indexes are not supported on counter tables"); - - if (table.isView()) - throw new InvalidRequestException("Secondary indexes are not supported on materialized views"); - - if (table.isCompactTable() && !table.isStaticCompactTable()) - throw new InvalidRequestException("Secondary indexes are not supported on COMPACT STORAGE tables that have clustering columns"); - - List<IndexTarget> targets = new ArrayList<>(rawTargets.size()); - for (IndexTarget.Raw rawTarget : rawTargets) - targets.add(rawTarget.prepare(table)); - - if (targets.isEmpty() && !properties.isCustom) - throw new InvalidRequestException("Only CUSTOM indexes can be created without specifying a target column"); - - if (targets.size() > 1) - validateTargetsForMultiColumnIndex(targets); - - for (IndexTarget target : targets) - { - ColumnMetadata cd = table.getColumn(target.column); - - if (cd == null) - throw new InvalidRequestException("No column definition found for column " + target.column); - - if (cd.type.referencesDuration()) - { - checkFalse(cd.type.isCollection(), "Secondary indexes are not supported on collections containing durations"); - checkFalse(cd.type.isTuple(), "Secondary indexes are not supported on tuples containing durations"); - checkFalse(cd.type.isUDT(), "Secondary indexes are not supported on UDTs containing durations"); - throw invalidRequest("Secondary indexes are not supported on duration columns"); - } - - // TODO: we could lift that limitation - if (table.isCompactTable() && cd.isPrimaryKeyColumn()) - throw new InvalidRequestException("Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables"); - - if (cd.kind == ColumnMetadata.Kind.PARTITION_KEY && table.partitionKeyColumns().size() == 1) - throw new InvalidRequestException(String.format("Cannot create secondary index on partition key column %s", target.column)); - - boolean isMap = cd.type instanceof MapType; - boolean isFrozenCollection = cd.type.isCollection() && !cd.type.isMultiCell(); - if (isFrozenCollection) - { - validateForFrozenCollection(target); - } - else - { - validateNotFullIndex(target); - validateIsSimpleIndexIfTargetColumnNotCollection(cd, target); - validateTargetColumnIsMapIfIndexInvolvesKeys(isMap, target); - } - - checkFalse(cd.type.isUDT() && cd.type.isMultiCell(), "Secondary indexes are not supported on non-frozen UDTs"); - } - - if (!Strings.isNullOrEmpty(indexName)) - { - if (Schema.instance.getKeyspaceMetadata(keyspace()).existingIndexNames(null).contains(indexName)) - { - if (ifNotExists) - return; - else - throw new InvalidRequestException(String.format("Index %s already exists", indexName)); - } - } - - properties.validate(); - } - - private void validateForFrozenCollection(IndexTarget target) throws InvalidRequestException - { - if (target.type != IndexTarget.Type.FULL) - throw new InvalidRequestException(String.format("Cannot create %s() index on frozen column %s. " + - "Frozen collections only support full() indexes", - target.type, target.column)); - } - - private void validateNotFullIndex(IndexTarget target) throws InvalidRequestException - { - if (target.type == IndexTarget.Type.FULL) - throw new InvalidRequestException("full() indexes can only be created on frozen collections"); - } - - private void validateIsSimpleIndexIfTargetColumnNotCollection(ColumnMetadata cd, IndexTarget target) throws InvalidRequestException - { - if (!cd.type.isCollection() && target.type != IndexTarget.Type.SIMPLE) - throw new InvalidRequestException(String.format("Cannot create %s() index on %s. " + - "Non-collection columns support only simple indexes", - target.type.toString(), target.column)); - } - - private void validateTargetColumnIsMapIfIndexInvolvesKeys(boolean isMap, IndexTarget target) throws InvalidRequestException - { - if (target.type == IndexTarget.Type.KEYS || target.type == IndexTarget.Type.KEYS_AND_VALUES) - { - if (!isMap) - throw new InvalidRequestException(String.format("Cannot create index on %s of column %s with non-map type", - target.type, target.column)); - } - } - - private void validateTargetsForMultiColumnIndex(List<IndexTarget> targets) - { - if (!properties.isCustom) - throw new InvalidRequestException("Only CUSTOM indexes support multiple columns"); - - Set<ColumnIdentifier> columns = Sets.newHashSetWithExpectedSize(targets.size()); - for (IndexTarget target : targets) - if (!columns.add(target.column)) - throw new InvalidRequestException("Duplicate column " + target.column + " in index target list"); - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException - { - TableMetadata current = Schema.instance.getTableMetadata(keyspace(), columnFamily()); - List<IndexTarget> targets = new ArrayList<>(rawTargets.size()); - for (IndexTarget.Raw rawTarget : rawTargets) - targets.add(rawTarget.prepare(current)); - - String acceptedName = indexName; - if (Strings.isNullOrEmpty(acceptedName)) - { - acceptedName = Indexes.getAvailableIndexName(keyspace(), - columnFamily(), - targets.size() == 1 ? targets.get(0).column.toString() : null); - } - - if (Schema.instance.getKeyspaceMetadata(keyspace()).existingIndexNames(null).contains(acceptedName)) - { - if (ifNotExists) - return null; - else - throw new InvalidRequestException(String.format("Index %s already exists", acceptedName)); - } - - IndexMetadata.Kind kind; - Map<String, String> indexOptions; - if (properties.isCustom) - { - kind = IndexMetadata.Kind.CUSTOM; - indexOptions = properties.getOptions(); - } - else - { - indexOptions = Collections.emptyMap(); - kind = current.isCompound() ? IndexMetadata.Kind.COMPOSITES : IndexMetadata.Kind.KEYS; - } - - IndexMetadata index = IndexMetadata.fromIndexTargets(targets, acceptedName, kind, indexOptions); - - // check to disallow creation of an index which duplicates an existing one in all but name - Optional<IndexMetadata> existingIndex = Iterables.tryFind(current.indexes, existing -> existing.equalsWithoutName(index)); - if (existingIndex.isPresent()) - { - if (ifNotExists) - return null; - else - throw new InvalidRequestException(String.format("Index %s is a duplicate of existing index %s", - index.name, - existingIndex.get().name)); - } - - TableMetadata updated = - current.unbuild() - .indexes(current.indexes.with(index)) - .build(); - - logger.trace("Updating index definition for {}", indexName); - - MigrationManager.announceTableUpdate(updated, isLocalOnly); - - // Creating an index is akin to updating the CF - return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily()); - } - - @Override - public String toString() - { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); - } - @Override - public AuditLogContext getAuditLogContext() - { - return new AuditLogContext(AuditLogEntryType.CREATE_INDEX, keyspace(), indexName); - } -} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
