http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/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 index ade2752..878195f 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java @@ -77,7 +77,7 @@ public final class CreateAggregateStatement extends SchemaAlteringStatement this.ifNotExists = ifNotExists; } - public Prepared prepare() + public CreateAggregateStatement prepare(ClientState state) { argTypes = new ArrayList<>(argRawTypes.size()); for (CQL3Type.Raw rawType : argRawTypes) @@ -135,7 +135,7 @@ public final class CreateAggregateStatement extends SchemaAlteringStatement throw new InvalidRequestException("INITCOND must not be empty for all types except TEXT, ASCII, BLOB"); } - return super.prepare(); + return this; } private AbstractType<?> prepareType(String typeName, CQL3Type.Raw rawType) @@ -143,16 +143,11 @@ public final class CreateAggregateStatement extends SchemaAlteringStatement 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 + public void setKeyspace(ClientState state) throws InvalidRequestException { if (!functionName.hasKeyspace() && state.getRawKeyspace() != null) functionName = new FunctionName(state.getKeyspace(), functionName.name); @@ -183,19 +178,19 @@ public final class CreateAggregateStatement extends SchemaAlteringStatement } } - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException + public void authorize(ClientState state) throws UnauthorizedException, InvalidRequestException { if (Schema.instance.findFunction(functionName, argTypes).isPresent() && orReplace) - state.ensureHasPermission(Permission.ALTER, FunctionResource.function(functionName.keyspace, - functionName.name, - argTypes)); + state.ensurePermission(Permission.ALTER, FunctionResource.function(functionName.keyspace, + functionName.name, + argTypes)); else - state.ensureHasPermission(Permission.CREATE, FunctionResource.keyspace(functionName.keyspace)); + state.ensurePermission(Permission.CREATE, FunctionResource.keyspace(functionName.keyspace)); - state.ensureHasPermission(Permission.EXECUTE, stateFunction); + state.ensurePermission(Permission.EXECUTE, stateFunction); if (finalFunction != null) - state.ensureHasPermission(Permission.EXECUTE, finalFunction); + state.ensurePermission(Permission.EXECUTE, finalFunction); } public void validate(ClientState state) throws InvalidRequestException
http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/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 index 2e1f78c..378ba9d 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java @@ -75,7 +75,7 @@ public final class CreateFunctionStatement extends SchemaAlteringStatement this.ifNotExists = ifNotExists; } - public Prepared prepare() throws InvalidRequestException + public CreateFunctionStatement prepare(ClientState state) { if (new HashSet<>(argNames).size() != argNames.size()) throw new InvalidRequestException(String.format("duplicate argument names for given function %s with argument names %s", @@ -86,10 +86,11 @@ public final class CreateFunctionStatement extends SchemaAlteringStatement argTypes.add(prepareType("arguments", rawType)); returnType = prepareType("return type", rawReturnType); - return super.prepare(); + + return this; } - public void prepareKeyspace(ClientState state) throws InvalidRequestException + public void setKeyspace(ClientState state) throws InvalidRequestException { if (!functionName.hasKeyspace() && state.getRawKeyspace() != null) functionName = new FunctionName(state.getRawKeyspace(), functionName.name); @@ -116,14 +117,14 @@ public final class CreateFunctionStatement extends SchemaAlteringStatement } } - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException + public void authorize(ClientState state) throws UnauthorizedException, InvalidRequestException { if (Schema.instance.findFunction(functionName, argTypes).isPresent() && orReplace) - state.ensureHasPermission(Permission.ALTER, FunctionResource.function(functionName.keyspace, - functionName.name, - argTypes)); + state.ensurePermission(Permission.ALTER, FunctionResource.function(functionName.keyspace, + functionName.name, + argTypes)); else - state.ensureHasPermission(Permission.CREATE, FunctionResource.keyspace(functionName.keyspace)); + state.ensurePermission(Permission.CREATE, FunctionResource.keyspace(functionName.keyspace)); } public void validate(ClientState state) throws InvalidRequestException @@ -172,11 +173,6 @@ public final class CreateFunctionStatement extends SchemaAlteringStatement 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; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/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 a0955af..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java +++ /dev/null @@ -1,258 +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.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 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.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()); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java deleted file mode 100644 index f859ff7..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java +++ /dev/null @@ -1,137 +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.regex.Pattern; - -import org.apache.cassandra.auth.*; -import org.apache.cassandra.config.DatabaseDescriptor; -import org.apache.cassandra.exceptions.*; -import org.apache.cassandra.locator.LocalStrategy; -import org.apache.cassandra.schema.KeyspaceMetadata; -import org.apache.cassandra.schema.KeyspaceParams; -import org.apache.cassandra.schema.MigrationManager; -import org.apache.cassandra.schema.Schema; -import org.apache.cassandra.schema.SchemaConstants; -import org.apache.cassandra.service.*; -import org.apache.cassandra.transport.Event; - -/** A <code>CREATE KEYSPACE</code> statement parsed from a CQL query. */ -public class CreateKeyspaceStatement extends SchemaAlteringStatement -{ - private static final Pattern PATTERN_WORD_CHARS = Pattern.compile("\\w+"); - - private final String name; - private final KeyspaceAttributes attrs; - private final boolean ifNotExists; - - /** - * Creates a new <code>CreateKeyspaceStatement</code> instance for a given - * keyspace name and keyword arguments. - * - * @param name the name of the keyspace to create - * @param attrs map of the raw keyword arguments that followed the <code>WITH</code> keyword. - */ - public CreateKeyspaceStatement(String name, KeyspaceAttributes attrs, boolean ifNotExists) - { - super(); - this.name = name; - this.attrs = attrs; - this.ifNotExists = ifNotExists; - } - - @Override - public String keyspace() - { - return name; - } - - public void checkAccess(ClientState state) throws UnauthorizedException - { - state.hasAllKeyspacesAccess(Permission.CREATE); - } - - /** - * The <code>CqlParser</code> only goes as far as extracting the keyword arguments - * from these statements, so this method is responsible for processing and - * validating. - * - * @throws InvalidRequestException if arguments are missing or unacceptable - */ - public void validate(ClientState state) throws RequestValidationException - { - Schema.validateKeyspaceNotSystem(name); - - // keyspace name - if (!PATTERN_WORD_CHARS.matcher(name).matches()) - throw new InvalidRequestException(String.format("\"%s\" is not a valid keyspace name", name)); - if (name.length() > SchemaConstants.NAME_LENGTH) - throw new InvalidRequestException(String.format("Keyspace names shouldn't be more than %s characters long (got \"%s\")", SchemaConstants.NAME_LENGTH, name)); - - attrs.validate(); - - if (attrs.getReplicationStrategyClass() == null) - throw new ConfigurationException("Missing mandatory replication strategy class"); - - // The strategy is validated through KSMetaData.validate() in announceNewKeyspace below. - // However, for backward compatibility with thrift, this doesn't validate unexpected options yet, - // so doing proper validation here. - KeyspaceParams params = attrs.asNewKeyspaceParams(); - params.validate(name); - if (params.replication.klass.equals(LocalStrategy.class)) - throw new ConfigurationException("Unable to use given strategy class: LocalStrategy is reserved for internal use."); - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException - { - KeyspaceMetadata ksm = KeyspaceMetadata.create(name, attrs.asNewKeyspaceParams()); - try - { - MigrationManager.announceNewKeyspace(ksm, isLocalOnly); - return new Event.SchemaChange(Event.SchemaChange.Change.CREATED, keyspace()); - } - catch (AlreadyExistsException e) - { - if (ifNotExists) - return null; - throw e; - } - } - - protected void grantPermissionsToCreator(QueryState state) - { - try - { - RoleResource role = RoleResource.role(state.getClientState().getUser().getName()); - DataResource keyspace = DataResource.keyspace(keyspace()); - DatabaseDescriptor.getAuthorizer().grant(AuthenticatedUser.SYSTEM_USER, - keyspace.applicablePermissions(), - keyspace, - role); - FunctionResource functions = FunctionResource.keyspace(keyspace()); - DatabaseDescriptor.getAuthorizer().grant(AuthenticatedUser.SYSTEM_USER, - functions.applicablePermissions(), - functions, - role); - } - catch (RequestExecutionException e) - { - throw new RuntimeException(e); - } - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/CreateRoleStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateRoleStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateRoleStatement.java index 9be4c89..03b30f8 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateRoleStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateRoleStatement.java @@ -37,7 +37,7 @@ public class CreateRoleStatement extends AuthenticationStatement this.ifNotExists = ifNotExists; } - public void checkAccess(ClientState state) throws UnauthorizedException + public void authorize(ClientState state) throws UnauthorizedException { super.checkPermission(state, Permission.CREATE, RoleResource.root()); if (opts.getSuperuser().isPresent()) @@ -54,7 +54,7 @@ public class CreateRoleStatement extends AuthenticationStatement if (role.getRoleName().isEmpty()) throw new InvalidRequestException("Role name can't be an empty string"); - // validate login here before checkAccess to avoid leaking role existence to anonymous users. + // validate login here before authorize to avoid leaking role existence to anonymous users. state.ensureNotAnonymous(); if (!ifNotExists && DatabaseDescriptor.getRoleManager().isExistingRole(role)) http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java deleted file mode 100644 index 1b09c0b..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java +++ /dev/null @@ -1,414 +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.*; -import java.util.regex.Pattern; -import com.google.common.collect.HashMultiset; -import com.google.common.collect.Multiset; -import org.apache.commons.lang3.StringUtils; - -import org.apache.cassandra.auth.*; -import org.apache.cassandra.config.*; -import org.apache.cassandra.cql3.*; -import org.apache.cassandra.db.*; -import org.apache.cassandra.db.marshal.*; -import org.apache.cassandra.exceptions.*; -import org.apache.cassandra.schema.*; -import org.apache.cassandra.service.ClientState; -import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.transport.Event; - -/** A {@code CREATE TABLE} parsed from a CQL query statement. */ -public class CreateTableStatement extends SchemaAlteringStatement -{ - private static final Pattern PATTERN_WORD_CHARS = Pattern.compile("\\w+"); - - private List<AbstractType<?>> keyTypes; - private List<AbstractType<?>> clusteringTypes; - - private final Map<ByteBuffer, AbstractType> multicellColumns = new HashMap<>(); - - private final List<ColumnIdentifier> keyAliases = new ArrayList<>(); - private final List<ColumnIdentifier> columnAliases = new ArrayList<>(); - - private boolean isDense; - private boolean isCompound; - private boolean hasCounters; - - // use a TreeMap to preserve ordering across JDK versions (see CASSANDRA-9492) - private final Map<ColumnIdentifier, AbstractType> columns = new TreeMap<>((o1, o2) -> o1.bytes.compareTo(o2.bytes)); - - private final Set<ColumnIdentifier> staticColumns; - private final TableParams params; - private final boolean ifNotExists; - private final TableId id; - - public CreateTableStatement(CFName name, TableParams params, boolean ifNotExists, Set<ColumnIdentifier> staticColumns, TableId id) - { - super(name); - this.params = params; - this.ifNotExists = ifNotExists; - this.staticColumns = staticColumns; - this.id = id; - } - - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException - { - state.hasKeyspaceAccess(keyspace(), Permission.CREATE); - } - - public void validate(ClientState state) - { - // validated in announceMigration() - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException - { - try - { - MigrationManager.announceNewTable(toTableMetadata(), isLocalOnly); - return new Event.SchemaChange(Event.SchemaChange.Change.CREATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily()); - } - catch (AlreadyExistsException e) - { - if (ifNotExists) - return null; - throw e; - } - } - - protected void grantPermissionsToCreator(QueryState state) - { - try - { - IResource resource = DataResource.table(keyspace(), columnFamily()); - DatabaseDescriptor.getAuthorizer().grant(AuthenticatedUser.SYSTEM_USER, - resource.applicablePermissions(), - resource, - RoleResource.role(state.getClientState().getUser().getName())); - } - catch (RequestExecutionException e) - { - throw new RuntimeException(e); - } - } - - /** - */ - public static TableMetadata.Builder parse(String cql, String keyspace) - { - CreateTableStatement.RawStatement raw = CQLFragmentParser.parseAny(CqlParser::createTableStatement, cql, "CREATE TABLE"); - raw.prepareKeyspace(keyspace); - CreateTableStatement prepared = (CreateTableStatement) raw.prepare(Types.none()).statement; - return prepared.builder(); - } - - public TableMetadata.Builder builder() - { - TableMetadata.Builder builder = TableMetadata.builder(keyspace(), columnFamily()); - - if (id != null) - builder.id(id); - - builder.isDense(isDense) - .isCompound(isCompound) - .isCounter(hasCounters) - .isSuper(false) - .isView(false) - .params(params); - - for (int i = 0; i < keyAliases.size(); i++) - builder.addPartitionKeyColumn(keyAliases.get(i), keyTypes.get(i)); - - for (int i = 0; i < columnAliases.size(); i++) - builder.addClusteringColumn(columnAliases.get(i), clusteringTypes.get(i)); - - boolean isStaticCompact = !isDense && !isCompound; - for (Map.Entry<ColumnIdentifier, AbstractType> entry : columns.entrySet()) - { - ColumnIdentifier name = entry.getKey(); - // Note that for "static" no-clustering compact storage we use static for the defined columns - if (staticColumns.contains(name) || isStaticCompact) - builder.addStaticColumn(name, entry.getValue()); - else - builder.addRegularColumn(name, entry.getValue()); - } - - boolean isCompactTable = isDense || !isCompound; - if (isCompactTable) - { - CompactTables.DefaultNames names = CompactTables.defaultNameGenerator(builder.columnNames()); - // Compact tables always have a clustering and a single regular value. - if (isStaticCompact) - { - builder.addClusteringColumn(names.defaultClusteringName(), UTF8Type.instance); - builder.addRegularColumn(names.defaultCompactValueName(), hasCounters ? CounterColumnType.instance : BytesType.instance); - } - else if (isDense && !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; - } - - /** - * Returns a TableMetadata instance based on the parameters parsed from this - * {@code CREATE} statement, or defaults where applicable. - * - * @return a TableMetadata instance corresponding to the values parsed from this statement - */ - public TableMetadata toTableMetadata() - { - return builder().build(); - } - - public static class RawStatement extends CFStatement - { - private final Map<ColumnIdentifier, CQL3Type.Raw> definitions = new HashMap<>(); - public final CFProperties properties = new CFProperties(); - - private final List<List<ColumnIdentifier>> keyAliases = new ArrayList<>(); - private final List<ColumnIdentifier> columnAliases = new ArrayList<>(); - private final Set<ColumnIdentifier> staticColumns = new HashSet<>(); - - private final Multiset<ColumnIdentifier> definedNames = HashMultiset.create(1); - - private final boolean ifNotExists; - - public RawStatement(CFName name, boolean ifNotExists) - { - super(name); - this.ifNotExists = ifNotExists; - } - - /** - * Transform this raw statement into a CreateTableStatement. - */ - public ParsedStatement.Prepared prepare() throws RequestValidationException - { - KeyspaceMetadata ksm = Schema.instance.getKeyspaceMetadata(keyspace()); - if (ksm == null) - throw new ConfigurationException(String.format("Keyspace %s doesn't exist", keyspace())); - return prepare(ksm.types); - } - - public ParsedStatement.Prepared prepare(Types udts) throws RequestValidationException - { - // Column family name - if (!PATTERN_WORD_CHARS.matcher(columnFamily()).matches()) - throw new InvalidRequestException(String.format("\"%s\" is not a valid table name (must be alphanumeric character or underscore only: [a-zA-Z_0-9]+)", columnFamily())); - if (columnFamily().length() > SchemaConstants.NAME_LENGTH) - throw new InvalidRequestException(String.format("Table names shouldn't be more than %s characters long (got \"%s\")", SchemaConstants.NAME_LENGTH, columnFamily())); - - for (Multiset.Entry<ColumnIdentifier> entry : definedNames.entrySet()) - if (entry.getCount() > 1) - throw new InvalidRequestException(String.format("Multiple definition of identifier %s", entry.getElement())); - - properties.validate(); - - TableParams params = properties.properties.asNewTableParams(); - - CreateTableStatement stmt = new CreateTableStatement(cfName, params, ifNotExists, staticColumns, properties.properties.getId()); - - for (Map.Entry<ColumnIdentifier, CQL3Type.Raw> entry : definitions.entrySet()) - { - ColumnIdentifier id = entry.getKey(); - CQL3Type pt = entry.getValue().prepare(keyspace(), udts); - if (pt.getType().isMultiCell()) - stmt.multicellColumns.put(id.bytes, pt.getType()); - if (entry.getValue().isCounter()) - stmt.hasCounters = true; - - // check for non-frozen UDTs or collections in a non-frozen UDT - if (pt.getType().isUDT() && pt.getType().isMultiCell()) - { - for (AbstractType<?> innerType : ((UserType) pt.getType()).fieldTypes()) - { - if (innerType.isMultiCell()) - { - assert innerType.isCollection(); // shouldn't get this far with a nested non-frozen UDT - throw new InvalidRequestException("Non-frozen UDTs with nested non-frozen collections are not supported"); - } - } - } - - stmt.columns.put(id, pt.getType()); // we'll remove what is not a column below - } - - if (keyAliases.isEmpty()) - throw new InvalidRequestException("No PRIMARY KEY specifed (exactly one required)"); - if (keyAliases.size() > 1) - throw new InvalidRequestException("Multiple PRIMARY KEYs specifed (exactly one required)"); - if (stmt.hasCounters && params.defaultTimeToLive > 0) - throw new InvalidRequestException("Cannot set default_time_to_live on a table with counters"); - - List<ColumnIdentifier> kAliases = keyAliases.get(0); - stmt.keyTypes = new ArrayList<>(kAliases.size()); - for (ColumnIdentifier alias : kAliases) - { - stmt.keyAliases.add(alias); - AbstractType<?> t = getTypeAndRemove(stmt.columns, alias); - if (t.asCQL3Type().getType() instanceof CounterColumnType) - throw new InvalidRequestException(String.format("counter type is not supported for PRIMARY KEY part %s", alias)); - if (t.asCQL3Type().getType().referencesDuration()) - throw new InvalidRequestException(String.format("duration type is not supported for PRIMARY KEY part %s", alias)); - if (staticColumns.contains(alias)) - throw new InvalidRequestException(String.format("Static column %s cannot be part of the PRIMARY KEY", alias)); - stmt.keyTypes.add(t); - } - - stmt.clusteringTypes = new ArrayList<>(columnAliases.size()); - // Handle column aliases - for (ColumnIdentifier t : columnAliases) - { - stmt.columnAliases.add(t); - - AbstractType<?> type = getTypeAndRemove(stmt.columns, t); - if (type.asCQL3Type().getType() instanceof CounterColumnType) - throw new InvalidRequestException(String.format("counter type is not supported for PRIMARY KEY part %s", t)); - if (type.asCQL3Type().getType().referencesDuration()) - throw new InvalidRequestException(String.format("duration type is not supported for PRIMARY KEY part %s", t)); - if (staticColumns.contains(t)) - throw new InvalidRequestException(String.format("Static column %s cannot be part of the PRIMARY KEY", t)); - stmt.clusteringTypes.add(type); - } - - // We've handled anything that is not a rpimary key so stmt.columns only contains NON-PK columns. So - // if it's a counter table, make sure we don't have non-counter types - if (stmt.hasCounters) - { - for (AbstractType<?> type : stmt.columns.values()) - if (!type.isCounter()) - throw new InvalidRequestException("Cannot mix counter and non counter columns in the same table"); - } - - boolean useCompactStorage = properties.useCompactStorage; - // 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). - stmt.isDense = useCompactStorage && !stmt.clusteringTypes.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"). - stmt.isCompound = !(useCompactStorage && stmt.clusteringTypes.size() <= 1); - - // For COMPACT STORAGE, we reject any "feature" that we wouldn't be able to translate back to thrift. - if (useCompactStorage) - { - if (!stmt.multicellColumns.isEmpty()) - throw new InvalidRequestException("Non-frozen collections and UDTs are not supported with COMPACT STORAGE"); - if (!staticColumns.isEmpty()) - throw new InvalidRequestException("Static columns are not supported in COMPACT STORAGE tables"); - - if (stmt.clusteringTypes.isEmpty()) - { - // It's a thrift "static CF" so there should be some columns definition - if (stmt.columns.isEmpty()) - throw new InvalidRequestException("No definition found that is not part of the PRIMARY KEY"); - } - - if (stmt.isDense) - { - // We can have no columns (only the PK), but we can't have more than one. - if (stmt.columns.size() > 1) - throw new InvalidRequestException(String.format("COMPACT STORAGE with composite PRIMARY KEY allows no more than one column not part of the PRIMARY KEY (got: %s)", StringUtils.join(stmt.columns.keySet(), ", "))); - } - else - { - // 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 (stmt.columns.isEmpty()) - throw new InvalidRequestException("COMPACT STORAGE with non-composite PRIMARY KEY require one column not part of the PRIMARY KEY, none given"); - } - } - else - { - if (stmt.clusteringTypes.isEmpty() && !staticColumns.isEmpty()) - { - // Static columns only make sense if we have at least one clustering column. Otherwise everything is static anyway - if (columnAliases.isEmpty()) - throw new InvalidRequestException("Static columns are only useful (and thus allowed) if the table has at least one clustering column"); - } - } - - // If we give a clustering order, we must explicitly do so for all aliases and in the order of the PK - if (!properties.definedOrdering.isEmpty()) - { - if (properties.definedOrdering.size() > columnAliases.size()) - throw new InvalidRequestException("Only clustering key columns can be defined in CLUSTERING ORDER directive"); - - int i = 0; - for (ColumnIdentifier id : properties.definedOrdering.keySet()) - { - ColumnIdentifier c = columnAliases.get(i); - if (!id.equals(c)) - { - if (properties.definedOrdering.containsKey(c)) - throw new InvalidRequestException(String.format("The order of columns in the CLUSTERING ORDER directive must be the one of the clustering key (%s must appear before %s)", c, id)); - else - throw new InvalidRequestException(String.format("Missing CLUSTERING ORDER for column %s", c)); - } - ++i; - } - } - - return new ParsedStatement.Prepared(stmt); - } - - private AbstractType<?> getTypeAndRemove(Map<ColumnIdentifier, AbstractType> columns, ColumnIdentifier t) throws InvalidRequestException - { - AbstractType type = columns.get(t); - if (type == null) - throw new InvalidRequestException(String.format("Unknown definition %s referenced in PRIMARY KEY", t)); - if (type.isMultiCell()) - { - if (type.isCollection()) - throw new InvalidRequestException(String.format("Invalid non-frozen collection type for PRIMARY KEY component %s", t)); - else - throw new InvalidRequestException(String.format("Invalid non-frozen user-defined type for PRIMARY KEY component %s", t)); - } - - columns.remove(t); - Boolean isReversed = properties.definedOrdering.get(t); - return isReversed != null && isReversed ? ReversedType.getInstance(type) : type; - } - - public void addDefinition(ColumnIdentifier def, CQL3Type.Raw type, boolean isStatic) - { - definedNames.add(def); - definitions.put(def, type); - if (isStatic) - staticColumns.add(def); - } - - public void addKeyAliases(List<ColumnIdentifier> aliases) - { - keyAliases.add(aliases); - } - - public void addColumnAlias(ColumnIdentifier alias) - { - columnAliases.add(alias); - } - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java deleted file mode 100644 index 0f12b9c..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/CreateTriggerStatement.java +++ /dev/null @@ -1,98 +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.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.apache.cassandra.cql3.CFName; -import org.apache.cassandra.exceptions.ConfigurationException; -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.TriggerMetadata; -import org.apache.cassandra.schema.Triggers; -import org.apache.cassandra.service.ClientState; -import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.transport.Event; -import org.apache.cassandra.triggers.TriggerExecutor; - -public class CreateTriggerStatement extends SchemaAlteringStatement -{ - private static final Logger logger = LoggerFactory.getLogger(CreateTriggerStatement.class); - - private final String triggerName; - private final String triggerClass; - private final boolean ifNotExists; - - public CreateTriggerStatement(CFName name, String triggerName, String clazz, boolean ifNotExists) - { - super(name); - this.triggerName = triggerName; - this.triggerClass = clazz; - this.ifNotExists = ifNotExists; - } - - public void checkAccess(ClientState state) throws UnauthorizedException - { - state.ensureIsSuper("Only superusers are allowed to perform CREATE TRIGGER queries"); - } - - public void validate(ClientState state) throws RequestValidationException - { - TableMetadata metadata = Schema.instance.validateTable(keyspace(), columnFamily()); - if (metadata.isView()) - throw new InvalidRequestException("Cannot CREATE TRIGGER against a materialized view"); - - try - { - TriggerExecutor.instance.loadTriggerInstance(triggerClass); - } - catch (Exception e) - { - throw new ConfigurationException(String.format("Trigger class '%s' doesn't exist", triggerClass)); - } - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws ConfigurationException, InvalidRequestException - { - TableMetadata current = Schema.instance.getTableMetadata(keyspace(), columnFamily()); - Triggers triggers = current.triggers; - - if (triggers.get(triggerName).isPresent()) - { - if (ifNotExists) - return null; - else - throw new InvalidRequestException(String.format("Trigger %s already exists", triggerName)); - } - - TableMetadata updated = - current.unbuild() - .triggers(triggers.with(TriggerMetadata.create(triggerName, triggerClass))) - .build(); - - logger.info("Adding trigger with name {} and class {}", triggerName, triggerClass); - - MigrationManager.announceTableUpdate(updated, isLocalOnly); - return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily()); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/CreateTypeStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateTypeStatement.java deleted file mode 100644 index 4462bc2..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/CreateTypeStatement.java +++ /dev/null @@ -1,135 +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 org.apache.cassandra.auth.Permission; -import org.apache.cassandra.cql3.*; -import org.apache.cassandra.db.marshal.AbstractType; -import org.apache.cassandra.db.marshal.UserType; -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.schema.Types; -import org.apache.cassandra.service.ClientState; -import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.transport.Event; - -public class CreateTypeStatement extends SchemaAlteringStatement -{ - private final UTName name; - private final List<FieldIdentifier> columnNames = new ArrayList<>(); - private final List<CQL3Type.Raw> columnTypes = new ArrayList<>(); - private final boolean ifNotExists; - - public CreateTypeStatement(UTName name, boolean ifNotExists) - { - super(); - this.name = name; - this.ifNotExists = ifNotExists; - } - - @Override - public void prepareKeyspace(ClientState state) throws InvalidRequestException - { - if (!name.hasKeyspace()) - name.setKeyspace(state.getKeyspace()); - } - - public void addDefinition(FieldIdentifier name, CQL3Type.Raw type) - { - columnNames.add(name); - columnTypes.add(type); - } - - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException - { - state.hasKeyspaceAccess(keyspace(), Permission.CREATE); - } - - public void validate(ClientState state) throws RequestValidationException - { - KeyspaceMetadata ksm = Schema.instance.getKeyspaceMetadata(name.getKeyspace()); - if (ksm == null) - throw new InvalidRequestException(String.format("Cannot add type in unknown keyspace %s", name.getKeyspace())); - - if (ksm.types.get(name.getUserTypeName()).isPresent() && !ifNotExists) - throw new InvalidRequestException(String.format("A user type of name %s already exists", name)); - - for (CQL3Type.Raw type : columnTypes) - { - if (type.isCounter()) - throw new InvalidRequestException("A user type cannot contain counters"); - if (type.isUDT() && !type.isFrozen()) - throw new InvalidRequestException("A user type cannot contain non-frozen UDTs"); - } - } - - public static void checkForDuplicateNames(UserType type) throws InvalidRequestException - { - for (int i = 0; i < type.size() - 1; i++) - { - FieldIdentifier fieldName = type.fieldName(i); - for (int j = i+1; j < type.size(); j++) - { - if (fieldName.equals(type.fieldName(j))) - throw new InvalidRequestException(String.format("Duplicate field name %s in type %s", fieldName, type.name)); - } - } - } - - public void addToRawBuilder(Types.RawBuilder builder) throws InvalidRequestException - { - builder.add(name.getStringTypeName(), - columnNames.stream().map(FieldIdentifier::toString).collect(Collectors.toList()), - columnTypes.stream().map(CQL3Type.Raw::toString).collect(Collectors.toList())); - } - - @Override - public String keyspace() - { - return name.getKeyspace(); - } - - public UserType createType() throws InvalidRequestException - { - List<AbstractType<?>> types = new ArrayList<>(columnTypes.size()); - for (CQL3Type.Raw type : columnTypes) - types.add(type.prepare(keyspace()).getType()); - - return new UserType(name.getKeyspace(), name.getUserTypeName(), columnNames, types, true); - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws InvalidRequestException, ConfigurationException - { - KeyspaceMetadata ksm = Schema.instance.getKeyspaceMetadata(name.getKeyspace()); - assert ksm != null; // should haven't validate otherwise - - // Can happen with ifNotExists - if (ksm.types.get(name.getUserTypeName()).isPresent()) - return null; - - UserType type = createType(); - checkForDuplicateNames(type); - MigrationManager.announceNewType(type, isLocalOnly); - return new Event.SchemaChange(Event.SchemaChange.Change.CREATED, Event.SchemaChange.Target.TYPE, keyspace(), name.getStringTypeName()); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java deleted file mode 100644 index 8e92534..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java +++ /dev/null @@ -1,338 +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.collect.Iterables; -import com.google.common.collect.Sets; - -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.db.marshal.AbstractType; -import org.apache.cassandra.db.marshal.DurationType; -import org.apache.cassandra.db.marshal.ReversedType; -import org.apache.cassandra.db.view.View; -import org.apache.cassandra.exceptions.AlreadyExistsException; -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.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 CreateViewStatement extends SchemaAlteringStatement -{ - private final CFName baseName; - private final List<RawSelector> selectClause; - private final WhereClause whereClause; - private final List<ColumnMetadata.Raw> partitionKeys; - private final List<ColumnMetadata.Raw> clusteringKeys; - public final CFProperties properties = new CFProperties(); - private final boolean ifNotExists; - - public CreateViewStatement(CFName viewName, - CFName baseName, - List<RawSelector> selectClause, - WhereClause whereClause, - List<ColumnMetadata.Raw> partitionKeys, - List<ColumnMetadata.Raw> clusteringKeys, - boolean ifNotExists) - { - super(viewName); - this.baseName = baseName; - this.selectClause = selectClause; - this.whereClause = whereClause; - this.partitionKeys = partitionKeys; - this.clusteringKeys = clusteringKeys; - this.ifNotExists = ifNotExists; - } - - - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException - { - if (!baseName.hasKeyspace()) - baseName.setKeyspace(keyspace(), true); - state.hasColumnFamilyAccess(keyspace(), baseName.getColumnFamily(), Permission.ALTER); - } - - public void validate(ClientState state) throws RequestValidationException - { - // We do validation in announceMigration to reduce doubling up of work - } - - private interface AddColumn - { - void add(ColumnIdentifier identifier, AbstractType<?> type); - } - - private void add(TableMetadata baseCfm, Iterable<ColumnIdentifier> columns, AddColumn adder) - { - for (ColumnIdentifier column : columns) - { - AbstractType<?> type = baseCfm.getColumn(column).type; - if (properties.definedOrdering.containsKey(column)) - { - boolean desc = properties.definedOrdering.get(column); - if (!desc && type.isReversed()) - { - type = ((ReversedType)type).baseType; - } - else if (desc && !type.isReversed()) - { - type = ReversedType.getInstance(type); - } - } - adder.add(column, type); - } - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException - { - // We need to make sure that: - // - primary key includes all columns in base table's primary key - // - make sure that the select statement does not have anything other than columns - // and their names match the base table's names - // - make sure that primary key does not include any collections - // - make sure there is no where clause in the select statement - // - make sure there is not currently a table or view - // - make sure baseTable gcGraceSeconds > 0 - - properties.validate(); - - if (properties.useCompactStorage) - throw new InvalidRequestException("Cannot use 'COMPACT STORAGE' when defining a materialized view"); - - // We enforce the keyspace because if the RF is different, the logic to wait for a - // specific replica would break - if (!baseName.getKeyspace().equals(keyspace())) - throw new InvalidRequestException("Cannot create a materialized view on a table in a separate keyspace"); - - TableMetadata metadata = Schema.instance.validateTable(baseName.getKeyspace(), baseName.getColumnFamily()); - - if (metadata.isCounter()) - throw new InvalidRequestException("Materialized views are not supported on counter tables"); - if (metadata.isView()) - throw new InvalidRequestException("Materialized views cannot be created against other materialized views"); - - if (metadata.params.gcGraceSeconds == 0) - { - throw new InvalidRequestException(String.format("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.", cfName.getColumnFamily(), - baseName.getColumnFamily())); - } - - Set<ColumnIdentifier> included = Sets.newHashSetWithExpectedSize(selectClause.size()); - for (RawSelector selector : selectClause) - { - Selectable.Raw selectable = selector.selectable; - if (selectable instanceof Selectable.WithFieldSelection.Raw) - throw new InvalidRequestException("Cannot select out a part of type when defining a materialized view"); - if (selectable instanceof Selectable.WithFunction.Raw) - throw new InvalidRequestException("Cannot use function when defining a materialized view"); - if (selectable instanceof Selectable.WritetimeOrTTL.Raw) - throw new InvalidRequestException("Cannot use function when defining a materialized view"); - if (selector.alias != null) - throw new InvalidRequestException("Cannot use alias when defining a materialized view"); - - Selectable s = selectable.prepare(metadata); - if (s instanceof Term.Raw) - throw new InvalidRequestException("Cannot use terms in selection when defining a materialized view"); - - ColumnMetadata cdef = (ColumnMetadata)s; - included.add(cdef.name); - } - - Set<ColumnMetadata.Raw> targetPrimaryKeys = new HashSet<>(); - for (ColumnMetadata.Raw identifier : Iterables.concat(partitionKeys, clusteringKeys)) - { - if (!targetPrimaryKeys.add(identifier)) - throw new InvalidRequestException("Duplicate entry found in PRIMARY KEY: "+identifier); - - ColumnMetadata cdef = identifier.prepare(metadata); - - if (cdef.type.isMultiCell()) - throw new InvalidRequestException(String.format("Cannot use MultiCell column '%s' in PRIMARY KEY of materialized view", identifier)); - - if (cdef.isStatic()) - throw new InvalidRequestException(String.format("Cannot use Static column '%s' in PRIMARY KEY of materialized view", identifier)); - - if (cdef.type instanceof DurationType) - throw new InvalidRequestException(String.format("Cannot use Duration column '%s' in PRIMARY KEY of materialized view", identifier)); - } - - // build the select statement - Map<ColumnMetadata.Raw, Boolean> orderings = Collections.emptyMap(); - List<ColumnMetadata.Raw> groups = Collections.emptyList(); - SelectStatement.Parameters parameters = new SelectStatement.Parameters(orderings, groups, false, true, false); - - SelectStatement.RawStatement rawSelect = new SelectStatement.RawStatement(baseName, parameters, selectClause, whereClause, null, null); - - ClientState state = ClientState.forInternalCalls(); - state.setKeyspace(keyspace()); - - rawSelect.prepareKeyspace(state); - rawSelect.setBoundVariables(getBoundVariables()); - - ParsedStatement.Prepared prepared = rawSelect.prepare(true); - SelectStatement select = (SelectStatement) prepared.statement; - StatementRestrictions restrictions = select.getRestrictions(); - - if (!prepared.boundNames.isEmpty()) - throw new InvalidRequestException("Cannot use query parameters in CREATE MATERIALIZED VIEW statements"); - - String whereClauseText = View.relationsToWhereClause(whereClause.relations); - - Set<ColumnIdentifier> basePrimaryKeyCols = new HashSet<>(); - for (ColumnMetadata definition : Iterables.concat(metadata.partitionKeyColumns(), metadata.clusteringColumns())) - basePrimaryKeyCols.add(definition.name); - - List<ColumnIdentifier> targetClusteringColumns = new ArrayList<>(); - List<ColumnIdentifier> targetPartitionKeys = new ArrayList<>(); - - // This is only used as an intermediate state; this is to catch whether multiple non-PK columns are used - boolean hasNonPKColumn = false; - for (ColumnMetadata.Raw raw : partitionKeys) - hasNonPKColumn |= getColumnIdentifier(metadata, basePrimaryKeyCols, hasNonPKColumn, raw, targetPartitionKeys, restrictions); - - for (ColumnMetadata.Raw raw : clusteringKeys) - hasNonPKColumn |= getColumnIdentifier(metadata, basePrimaryKeyCols, hasNonPKColumn, raw, targetClusteringColumns, restrictions); - - // 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. - boolean missingClusteringColumns = false; - StringBuilder columnNames = new StringBuilder(); - List<ColumnIdentifier> includedColumns = new ArrayList<>(); - for (ColumnMetadata def : metadata.columns()) - { - ColumnIdentifier identifier = def.name; - boolean includeDef = included.isEmpty() || included.contains(identifier); - - if (includeDef && def.isStatic()) - { - throw new InvalidRequestException(String.format("Unable to include static column '%s' which would be included by Materialized View SELECT * statement", identifier)); - } - - boolean defInTargetPrimaryKey = targetClusteringColumns.contains(identifier) - || targetPartitionKeys.contains(identifier); - - if (includeDef && !defInTargetPrimaryKey) - { - includedColumns.add(identifier); - } - if (!def.isPrimaryKeyColumn()) continue; - - if (!defInTargetPrimaryKey) - { - if (missingClusteringColumns) - columnNames.append(','); - else - missingClusteringColumns = true; - columnNames.append(identifier); - } - } - if (missingClusteringColumns) - throw new InvalidRequestException(String.format("Cannot create Materialized View %s without primary key columns from base %s (%s)", - columnFamily(), baseName.getColumnFamily(), columnNames.toString())); - - if (targetPartitionKeys.isEmpty()) - throw new InvalidRequestException("Must select at least a column for a Materialized View"); - - if (targetClusteringColumns.isEmpty()) - throw new InvalidRequestException("No columns are defined for Materialized View other than primary key"); - - TableParams params = properties.properties.asNewTableParams(); - - if (params.defaultTimeToLive > 0) - { - throw new InvalidRequestException("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."); - } - - TableMetadata.Builder builder = - TableMetadata.builder(keyspace(), columnFamily(), properties.properties.getId()) - .isView(true) - .params(params); - - add(metadata, targetPartitionKeys, builder::addPartitionKeyColumn); - add(metadata, targetClusteringColumns, builder::addClusteringColumn); - add(metadata, includedColumns, builder::addRegularColumn); - - ViewMetadata definition = new ViewMetadata(keyspace(), - columnFamily(), - metadata.id, - metadata.name, - included.isEmpty(), - rawSelect, - whereClauseText, - builder.build()); - - try - { - MigrationManager.announceNewView(definition, isLocalOnly); - return new Event.SchemaChange(Event.SchemaChange.Change.CREATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily()); - } - catch (AlreadyExistsException e) - { - if (ifNotExists) - return null; - throw e; - } - } - - private static boolean getColumnIdentifier(TableMetadata cfm, - Set<ColumnIdentifier> basePK, - boolean hasNonPKColumn, - ColumnMetadata.Raw raw, - List<ColumnIdentifier> columns, - StatementRestrictions restrictions) - { - ColumnMetadata def = raw.prepare(cfm); - - boolean isPk = basePK.contains(def.name); - if (!isPk && hasNonPKColumn) - throw new InvalidRequestException(String.format("Cannot include more than one non-primary key column '%s' in materialized view primary key", def.name)); - - // We don't need to include the "IS NOT NULL" filter on a non-composite partition key - // because we will never allow a single partition key to be NULL - boolean isSinglePartitionKey = def.isPartitionKey() - && cfm.partitionKeyColumns().size() == 1; - if (!isSinglePartitionKey && !restrictions.isRestricted(def)) - throw new InvalidRequestException(String.format("Primary key column '%s' is required to be filtered by 'IS NOT NULL'", def.name)); - - columns.add(def.name); - return !isPk; - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java index e880bf8..ed1b521 100644 --- a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java @@ -39,14 +39,14 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue; */ public class DeleteStatement extends ModificationStatement { - private DeleteStatement(int boundTerms, + private DeleteStatement(VariableSpecifications bindVariables, TableMetadata cfm, Operations operations, StatementRestrictions restrictions, Conditions conditions, Attributes attrs) { - super(StatementType.DELETE, boundTerms, cfm, operations, restrictions, conditions, attrs); + super(StatementType.DELETE, bindVariables, cfm, operations, restrictions, conditions, attrs); } @Override @@ -122,7 +122,7 @@ public class DeleteStatement extends ModificationStatement private final List<Operation.RawDeletion> deletions; private final WhereClause whereClause; - public Parsed(CFName name, + public Parsed(QualifiedName name, Attributes.Raw attrs, List<Operation.RawDeletion> deletions, WhereClause whereClause, @@ -137,7 +137,7 @@ public class DeleteStatement extends ModificationStatement @Override protected ModificationStatement prepareInternal(TableMetadata metadata, - VariableSpecifications boundNames, + VariableSpecifications bindVariables, Conditions conditions, Attributes attrs) { @@ -152,17 +152,17 @@ public class DeleteStatement extends ModificationStatement checkFalse(def.isPrimaryKeyColumn(), "Invalid identifier %s for deletion (should not be a PRIMARY KEY part)", def.name); Operation op = deletion.prepare(metadata.keyspace, def, metadata); - op.collectMarkerSpecification(boundNames); + op.collectMarkerSpecification(bindVariables); operations.add(op); } StatementRestrictions restrictions = newRestrictions(metadata, - boundNames, + bindVariables, operations, whereClause, conditions); - DeleteStatement stmt = new DeleteStatement(boundNames.size(), + DeleteStatement stmt = new DeleteStatement(bindVariables, metadata, operations, restrictions, http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/DropAggregateStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/DropAggregateStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropAggregateStatement.java deleted file mode 100644 index 1a02903..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/DropAggregateStatement.java +++ /dev/null @@ -1,152 +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.Collection; -import java.util.List; - -import org.apache.cassandra.auth.Permission; -import org.apache.cassandra.cql3.CQL3Type; -import org.apache.cassandra.cql3.functions.*; -import org.apache.cassandra.db.marshal.AbstractType; -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.service.ClientState; -import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.transport.Event; - -/** - * A {@code DROP AGGREGATE} statement parsed from a CQL query. - */ -public final class DropAggregateStatement extends SchemaAlteringStatement -{ - private FunctionName functionName; - private final boolean ifExists; - private final List<CQL3Type.Raw> argRawTypes; - private final boolean argsPresent; - - public DropAggregateStatement(FunctionName functionName, - List<CQL3Type.Raw> argRawTypes, - boolean argsPresent, - boolean ifExists) - { - this.functionName = functionName; - this.argRawTypes = argRawTypes; - this.argsPresent = argsPresent; - this.ifExists = ifExists; - } - - 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); - } - - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException - { - // TODO CASSANDRA-7557 (function DDL permission) - - state.hasKeyspaceAccess(functionName.keyspace, Permission.DROP); - } - - public void validate(ClientState state) throws RequestValidationException - { - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException - { - Collection<Function> olds = Schema.instance.getFunctions(functionName); - - if (!argsPresent && olds != null && olds.size() > 1) - throw new InvalidRequestException(String.format("'DROP AGGREGATE %s' matches multiple function definitions; " + - "specify the argument types by issuing a statement like " + - "'DROP AGGREGATE %s (type, type, ...)'. Hint: use cqlsh " + - "'DESCRIBE AGGREGATE %s' command to find all overloads", - functionName, functionName, functionName)); - - Function old = null; - if (argsPresent) - { - if (Schema.instance.getKeyspaceMetadata(functionName.keyspace) != null) - { - List<AbstractType<?>> argTypes = new ArrayList<>(argRawTypes.size()); - for (CQL3Type.Raw rawType : argRawTypes) - argTypes.add(prepareType("arguments", rawType)); - - old = Schema.instance.findFunction(functionName, argTypes).orElse(null); - } - if (old == null || !(old instanceof AggregateFunction)) - { - if (ifExists) - return null; - // just build a nicer error message - StringBuilder sb = new StringBuilder(); - for (CQL3Type.Raw rawType : argRawTypes) - { - if (sb.length() > 0) - sb.append(", "); - sb.append(rawType); - } - throw new InvalidRequestException(String.format("Cannot drop non existing aggregate '%s(%s)'", - functionName, sb)); - } - } - else - { - if (olds == null || olds.isEmpty() || !(olds.iterator().next() instanceof AggregateFunction)) - { - if (ifExists) - return null; - throw new InvalidRequestException(String.format("Cannot drop non existing aggregate '%s'", functionName)); - } - old = olds.iterator().next(); - } - - if (old.isNative()) - throw new InvalidRequestException(String.format("Cannot drop aggregate '%s' because it is a " + - "native (built-in) function", functionName)); - - MigrationManager.announceAggregateDrop((UDAggregate)old, isLocalOnly); - return new Event.SchemaChange(Event.SchemaChange.Change.DROPPED, Event.SchemaChange.Target.AGGREGATE, - old.name().keyspace, old.name().name, AbstractType.asCQLTypeStringList(old.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; - } -} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
