http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/QueryProcessor.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/QueryProcessor.java b/src/java/org/apache/cassandra/cql3/QueryProcessor.java index cca93ff..6347f9c 100644 --- a/src/java/org/apache/cassandra/cql3/QueryProcessor.java +++ b/src/java/org/apache/cassandra/cql3/QueryProcessor.java @@ -69,11 +69,11 @@ public class QueryProcessor implements QueryHandler private static final Logger logger = LoggerFactory.getLogger(QueryProcessor.class); - private static final Cache<MD5Digest, ParsedStatement.Prepared> preparedStatements; + private static final Cache<MD5Digest, Prepared> preparedStatements; // A map for prepared statements used internally (which we don't want to mix with user statement, in particular we don't // bother with expiration on those. - private static final ConcurrentMap<String, ParsedStatement.Prepared> internalStatements = new ConcurrentHashMap<>(); + private static final ConcurrentMap<String, Prepared> internalStatements = new ConcurrentHashMap<>(); // Direct calls to processStatement do not increment the preparedStatementsExecuted/regularStatementsExecuted // counters. Callers of processStatement are responsible for correctly notifying metrics @@ -117,7 +117,7 @@ public class QueryProcessor implements QueryHandler } // Work around initialization dependency - private static enum InternalStateInstance + private enum InternalStateInstance { INSTANCE; @@ -125,9 +125,7 @@ public class QueryProcessor implements QueryHandler InternalStateInstance() { - ClientState state = ClientState.forInternalCalls(); - state.setKeyspace(SchemaConstants.SYSTEM_KEYSPACE_NAME); - this.queryState = new QueryState(state); + queryState = new QueryState(ClientState.forInternalCalls(SchemaConstants.SYSTEM_KEYSPACE_NAME)); } } @@ -167,7 +165,7 @@ public class QueryProcessor implements QueryHandler Schema.instance.registerListener(new StatementInvalidatingListener()); } - public ParsedStatement.Prepared getPrepared(MD5Digest id) + public Prepared getPrepared(MD5Digest id) { return preparedStatements.getIfPresent(id); } @@ -194,7 +192,7 @@ public class QueryProcessor implements QueryHandler { logger.trace("Process {} @CL.{}", statement, options.getConsistency()); ClientState clientState = queryState.getClientState(); - statement.checkAccess(clientState); + statement.authorize(clientState); statement.validate(clientState); ResultMessage result = statement.execute(queryState, options, queryStartNanoTime); @@ -219,10 +217,9 @@ public class QueryProcessor implements QueryHandler public ResultMessage process(String queryString, QueryState queryState, QueryOptions options, long queryStartNanoTime) throws RequestExecutionException, RequestValidationException { - ParsedStatement.Prepared p = getStatement(queryString, queryState.getClientState().cloneWithKeyspaceIfSet(options.getKeyspace())); - options.prepare(p.boundNames); - CQLStatement prepared = p.statement; - if (prepared.getBoundTerms() != options.getValues().size()) + CQLStatement prepared = getStatement(queryString, queryState.getClientState().cloneWithKeyspaceIfSet(options.getKeyspace())); + options.prepare(prepared.getBindVariables()); + if (prepared.getBindVariables().size() != options.getValues().size()) throw new InvalidRequestException("Invalid amount of bind variables"); if (!queryState.getClientState().isInternal) @@ -231,7 +228,7 @@ public class QueryProcessor implements QueryHandler return processStatement(prepared, queryState, options, queryStartNanoTime); } - public static ParsedStatement.Prepared parseStatement(String queryStr, ClientState clientState) throws RequestValidationException + public static CQLStatement parseStatement(String queryStr, ClientState clientState) throws RequestValidationException { return getStatement(queryStr, clientState); } @@ -250,43 +247,45 @@ public class QueryProcessor implements QueryHandler return null; } - private static QueryOptions makeInternalOptions(ParsedStatement.Prepared prepared, Object[] values) + private static QueryOptions makeInternalOptions(CQLStatement prepared, Object[] values) { return makeInternalOptions(prepared, values, ConsistencyLevel.ONE); } - private static QueryOptions makeInternalOptions(ParsedStatement.Prepared prepared, Object[] values, ConsistencyLevel cl) + private static QueryOptions makeInternalOptions(CQLStatement prepared, Object[] values, ConsistencyLevel cl) { - if (prepared.boundNames.size() != values.length) - throw new IllegalArgumentException(String.format("Invalid number of values. Expecting %d but got %d", prepared.boundNames.size(), values.length)); + if (prepared.getBindVariables().size() != values.length) + throw new IllegalArgumentException(String.format("Invalid number of values. Expecting %d but got %d", prepared.getBindVariables().size(), values.length)); - List<ByteBuffer> boundValues = new ArrayList<ByteBuffer>(values.length); + List<ByteBuffer> boundValues = new ArrayList<>(values.length); for (int i = 0; i < values.length; i++) { Object value = values[i]; - AbstractType type = prepared.boundNames.get(i).type; + AbstractType type = prepared.getBindVariables().get(i).type; boundValues.add(value instanceof ByteBuffer || value == null ? (ByteBuffer)value : type.decompose(value)); } return QueryOptions.forInternalCalls(cl, boundValues); } - public static ParsedStatement.Prepared prepareInternal(String query) throws RequestValidationException + public static Prepared prepareInternal(String query) throws RequestValidationException { - ParsedStatement.Prepared prepared = internalStatements.get(query); + Prepared prepared = internalStatements.get(query); if (prepared != null) return prepared; // Note: if 2 threads prepare the same query, we'll live so don't bother synchronizing - prepared = parseStatement(query, internalQueryState().getClientState()); - prepared.statement.validate(internalQueryState().getClientState()); - internalStatements.putIfAbsent(query, prepared); + CQLStatement statement = parseStatement(query, internalQueryState().getClientState()); + statement.validate(internalQueryState().getClientState()); + + prepared = new Prepared(statement); + internalStatements.put(query, prepared); return prepared; } public static UntypedResultSet executeInternal(String query, Object... values) { - ParsedStatement.Prepared prepared = prepareInternal(query); - ResultMessage result = prepared.statement.executeInternal(internalQueryState(), makeInternalOptions(prepared, values)); + Prepared prepared = prepareInternal(query); + ResultMessage result = prepared.statement.executeLocally(internalQueryState(), makeInternalOptions(prepared.statement, values)); if (result instanceof ResultMessage.Rows) return UntypedResultSet.create(((ResultMessage.Rows)result).result); else @@ -304,8 +303,8 @@ public class QueryProcessor implements QueryHandler { try { - ParsedStatement.Prepared prepared = prepareInternal(query); - ResultMessage result = prepared.statement.execute(state, makeInternalOptions(prepared, values, cl), System.nanoTime()); + Prepared prepared = prepareInternal(query); + ResultMessage result = prepared.statement.execute(state, makeInternalOptions(prepared.statement, values, cl), System.nanoTime()); if (result instanceof ResultMessage.Rows) return UntypedResultSet.create(((ResultMessage.Rows)result).result); else @@ -319,24 +318,24 @@ public class QueryProcessor implements QueryHandler public static UntypedResultSet executeInternalWithPaging(String query, int pageSize, Object... values) { - ParsedStatement.Prepared prepared = prepareInternal(query); + Prepared prepared = prepareInternal(query); if (!(prepared.statement instanceof SelectStatement)) throw new IllegalArgumentException("Only SELECTs can be paged"); SelectStatement select = (SelectStatement)prepared.statement; - QueryPager pager = select.getQuery(makeInternalOptions(prepared, values), FBUtilities.nowInSeconds()).getPager(null, ProtocolVersion.CURRENT); + QueryPager pager = select.getQuery(makeInternalOptions(prepared.statement, values), FBUtilities.nowInSeconds()).getPager(null, ProtocolVersion.CURRENT); return UntypedResultSet.create(select, pager, pageSize); } /** - * Same than executeInternal, but to use for queries we know are only executed once so that the + * Same than executeLocally, but to use for queries we know are only executed once so that the * created statement object is not cached. */ public static UntypedResultSet executeOnceInternal(String query, Object... values) { - ParsedStatement.Prepared prepared = parseStatement(query, internalQueryState().getClientState()); - prepared.statement.validate(internalQueryState().getClientState()); - ResultMessage result = prepared.statement.executeInternal(internalQueryState(), makeInternalOptions(prepared, values)); + CQLStatement statement = parseStatement(query, internalQueryState().getClientState()); + statement.validate(internalQueryState().getClientState()); + ResultMessage result = statement.executeLocally(internalQueryState(), makeInternalOptions(statement, values)); if (result instanceof ResultMessage.Rows) return UntypedResultSet.create(((ResultMessage.Rows)result).result); else @@ -344,16 +343,16 @@ public class QueryProcessor implements QueryHandler } /** - * A special version of executeInternal that takes the time used as "now" for the query in argument. + * A special version of executeLocally that takes the time used as "now" for the query in argument. * Note that this only make sense for Selects so this only accept SELECT statements and is only useful in rare * cases. */ public static UntypedResultSet executeInternalWithNow(int nowInSec, long queryStartNanoTime, String query, Object... values) { - ParsedStatement.Prepared prepared = prepareInternal(query); + Prepared prepared = prepareInternal(query); assert prepared.statement instanceof SelectStatement; SelectStatement select = (SelectStatement)prepared.statement; - ResultMessage result = select.executeInternal(internalQueryState(), makeInternalOptions(prepared, values), nowInSec, queryStartNanoTime); + ResultMessage result = select.executeInternal(internalQueryState(), makeInternalOptions(prepared.statement, values), nowInSec, queryStartNanoTime); assert result instanceof ResultMessage.Rows; return UntypedResultSet.create(((ResultMessage.Rows)result).result); } @@ -367,7 +366,7 @@ public class QueryProcessor implements QueryHandler { try (PartitionIterator iter = partitions) { - SelectStatement ss = (SelectStatement) getStatement(query, null).statement; + SelectStatement ss = (SelectStatement) getStatement(query, null); ResultSet cqlRows = ss.process(iter, FBUtilities.nowInSeconds()); return UntypedResultSet.create(cqlRows); } @@ -386,12 +385,13 @@ public class QueryProcessor implements QueryHandler if (existing != null) return existing; - ParsedStatement.Prepared prepared = getStatement(queryString, clientState); - prepared.rawCQLStatement = queryString; - int boundTerms = prepared.statement.getBoundTerms(); + CQLStatement statement = getStatement(queryString, clientState); + Prepared prepared = new Prepared(statement, queryString); + + int boundTerms = statement.getBindVariables().size(); if (boundTerms > FBUtilities.MAX_UNSIGNED_SHORT) throw new InvalidRequestException(String.format("Too many markers(?). %d markers exceed the allowed maximum of %d", boundTerms, FBUtilities.MAX_UNSIGNED_SHORT)); - assert boundTerms == prepared.boundNames.size(); + assert boundTerms == statement.getBindVariables().size(); return storePreparedStatement(queryString, clientState.getRawKeyspace(), prepared); } @@ -406,16 +406,16 @@ public class QueryProcessor implements QueryHandler throws InvalidRequestException { MD5Digest statementId = computeId(queryString, keyspace); - ParsedStatement.Prepared existing = preparedStatements.getIfPresent(statementId); + Prepared existing = preparedStatements.getIfPresent(statementId); if (existing == null) return null; checkTrue(queryString.equals(existing.rawCQLStatement), - String.format("MD5 hash collision: query with the same MD5 hash was already prepared. \n Existing: '%s'", existing.rawCQLStatement)); - return new ResultMessage.Prepared(statementId, existing); + String.format("MD5 hash collision: query with the same MDnt5 hash was already prepared. \n Existing: '%s'", existing.rawCQLStatement)); + return new ResultMessage.Prepared(statementId, existing.statement); } - private static ResultMessage.Prepared storePreparedStatement(String queryString, String keyspace, ParsedStatement.Prepared prepared) + private static ResultMessage.Prepared storePreparedStatement(String queryString, String keyspace, Prepared prepared) throws InvalidRequestException { // Concatenate the current keyspace so we don't mix prepared statements between keyspace (#5352). @@ -430,7 +430,7 @@ public class QueryProcessor implements QueryHandler MD5Digest statementId = computeId(queryString, keyspace); preparedStatements.put(statementId, prepared); SystemKeyspace.writePreparedStatement(keyspace, statementId, queryString); - return new ResultMessage.Prepared(statementId, prepared); + return new ResultMessage.Prepared(statementId, prepared.statement); } public ResultMessage processPrepared(CQLStatement statement, @@ -448,11 +448,11 @@ public class QueryProcessor implements QueryHandler { List<ByteBuffer> variables = options.getValues(); // Check to see if there are any bound variables to verify - if (!(variables.isEmpty() && (statement.getBoundTerms() == 0))) + if (!(variables.isEmpty() && statement.getBindVariables().isEmpty())) { - if (variables.size() != statement.getBoundTerms()) + if (variables.size() != statement.getBindVariables().size()) throw new InvalidRequestException(String.format("there were %d markers(?) in CQL but %d bound variables", - statement.getBoundTerms(), + statement.getBindVariables().size(), variables.size())); // at this point there is a match in count between markers and variables that is non-zero @@ -480,31 +480,31 @@ public class QueryProcessor implements QueryHandler throws RequestExecutionException, RequestValidationException { ClientState clientState = queryState.getClientState().cloneWithKeyspaceIfSet(options.getKeyspace()); - batch.checkAccess(clientState); + batch.authorize(clientState); batch.validate(); batch.validate(clientState); return batch.execute(queryState, options, queryStartNanoTime); } - public static ParsedStatement.Prepared getStatement(String queryStr, ClientState clientState) + public static CQLStatement getStatement(String queryStr, ClientState clientState) throws RequestValidationException { Tracing.trace("Parsing {}", queryStr); - ParsedStatement statement = parseStatement(queryStr); + CQLStatement.Raw statement = parseStatement(queryStr); // Set keyspace for statement that require login - if (statement instanceof CFStatement) - ((CFStatement) statement).prepareKeyspace(clientState); + if (statement instanceof QualifiedStatement) + ((QualifiedStatement) statement).setKeyspace(clientState); Tracing.trace("Preparing statement"); - return statement.prepare(); + return statement.prepare(clientState); } - public static <T extends ParsedStatement> T parseStatement(String queryStr, Class<T> klass, String type) throws SyntaxException + public static <T extends CQLStatement.Raw> T parseStatement(String queryStr, Class<T> klass, String type) throws SyntaxException { try { - ParsedStatement stmt = parseStatement(queryStr); + CQLStatement.Raw stmt = parseStatement(queryStr); if (!klass.isAssignableFrom(stmt.getClass())) throw new IllegalArgumentException("Invalid query, must be a " + type + " statement but was: " + stmt.getClass()); @@ -516,7 +516,7 @@ public class QueryProcessor implements QueryHandler throw new IllegalArgumentException(e.getMessage(), e); } } - public static ParsedStatement parseStatement(String queryStr) throws SyntaxException + public static CQLStatement.Raw parseStatement(String queryStr) throws SyntaxException { try { @@ -540,7 +540,7 @@ public class QueryProcessor implements QueryHandler } } - private static int measure(Object key, ParsedStatement.Prepared value) + private static int measure(Object key, Prepared value) { return Ints.checkedCast(ObjectSizes.measureDeep(key) + ObjectSizes.measureDeep(value)); } @@ -554,6 +554,30 @@ public class QueryProcessor implements QueryHandler internalStatements.clear(); } + public static class Prepared + { + public final CQLStatement statement; + + /** + * Contains the CQL statement source if the statement has been "regularly" perpared via + * {@link QueryProcessor#prepare(String, ClientState)} / + * {@link QueryHandler#prepare(String, ClientState, Map)}. + * Other usages of this class may or may not contain the CQL statement source. + */ + public final String rawCQLStatement; + + private Prepared(CQLStatement statement) + { + this(statement, ""); + } + + private Prepared(CQLStatement statement, String rawCQLStatement) + { + this.statement = statement; + this.rawCQLStatement = rawCQLStatement; + } + } + private static class StatementInvalidatingListener extends SchemaChangeListener { private static void removeInvalidPreparedStatements(String ksName, String cfName) @@ -566,10 +590,10 @@ public class QueryProcessor implements QueryHandler { Predicate<Function> matchesFunction = f -> ksName.equals(f.name().keyspace) && functionName.equals(f.name().name); - for (Iterator<Map.Entry<MD5Digest, ParsedStatement.Prepared>> iter = preparedStatements.asMap().entrySet().iterator(); + for (Iterator<Map.Entry<MD5Digest, Prepared>> iter = preparedStatements.asMap().entrySet().iterator(); iter.hasNext();) { - Map.Entry<MD5Digest, ParsedStatement.Prepared> pstmt = iter.next(); + Map.Entry<MD5Digest, Prepared> pstmt = iter.next(); if (Iterables.any(pstmt.getValue().statement.getFunctions(), matchesFunction)) { SystemKeyspace.removePreparedStatement(pstmt.getKey()); @@ -582,12 +606,12 @@ public class QueryProcessor implements QueryHandler statement -> Iterables.any(statement.statement.getFunctions(), matchesFunction)); } - private static void removeInvalidPersistentPreparedStatements(Iterator<Map.Entry<MD5Digest, ParsedStatement.Prepared>> iterator, + private static void removeInvalidPersistentPreparedStatements(Iterator<Map.Entry<MD5Digest, Prepared>> iterator, String ksName, String cfName) { while (iterator.hasNext()) { - Map.Entry<MD5Digest, ParsedStatement.Prepared> entry = iterator.next(); + Map.Entry<MD5Digest, Prepared> entry = iterator.next(); if (shouldInvalidate(ksName, cfName, entry.getValue().statement)) { SystemKeyspace.removePreparedStatement(entry.getKey()); @@ -596,7 +620,7 @@ public class QueryProcessor implements QueryHandler } } - private static void removeInvalidPreparedStatements(Iterator<ParsedStatement.Prepared> iterator, String ksName, String cfName) + private static void removeInvalidPreparedStatements(Iterator<Prepared> iterator, String ksName, String cfName) { while (iterator.hasNext()) {
http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java b/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java index 7bd7aac..84860f1 100644 --- a/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java +++ b/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java @@ -149,7 +149,7 @@ public final class SingleColumnRelation extends Relation entityAsString = String.format("%s[%s]", entityAsString, mapKey); if (isIN()) - return String.format("%s IN %s", entityAsString, inValues); + return String.format("%s IN %s", entityAsString, Tuples.tupleToString(inValues)); return String.format("%s %s %s", entityAsString, relationType, value); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/VariableSpecifications.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/VariableSpecifications.java b/src/java/org/apache/cassandra/cql3/VariableSpecifications.java index 96290a6..3cf0e5d 100644 --- a/src/java/org/apache/cassandra/cql3/VariableSpecifications.java +++ b/src/java/org/apache/cassandra/cql3/VariableSpecifications.java @@ -27,13 +27,13 @@ import org.apache.cassandra.schema.TableMetadata; public class VariableSpecifications { private final List<ColumnIdentifier> variableNames; - private final ColumnSpecification[] specs; + private final List<ColumnSpecification> specs; private final ColumnMetadata[] targetColumns; public VariableSpecifications(List<ColumnIdentifier> variableNames) { this.variableNames = variableNames; - this.specs = new ColumnSpecification[variableNames.size()]; + this.specs = Arrays.asList(new ColumnSpecification[variableNames.size()]); this.targetColumns = new ColumnMetadata[variableNames.size()]; } @@ -43,17 +43,17 @@ public class VariableSpecifications */ public static VariableSpecifications empty() { - return new VariableSpecifications(Collections.<ColumnIdentifier> emptyList()); + return new VariableSpecifications(Collections.emptyList()); } - public int size() + public boolean isEmpty() { - return variableNames.size(); + return variableNames.isEmpty(); } public List<ColumnSpecification> getSpecifications() { - return Arrays.asList(specs); + return specs; } /** @@ -94,12 +94,12 @@ public class VariableSpecifications // Use the user name, if there is one if (bindMarkerName != null) spec = new ColumnSpecification(spec.ksName, spec.cfName, bindMarkerName, spec.type); - specs[bindIndex] = spec; + specs.set(bindIndex, spec); } @Override public String toString() { - return Arrays.toString(specs); + return specs.toString(); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/WhereClause.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/WhereClause.java b/src/java/org/apache/cassandra/cql3/WhereClause.java index 9d4e51a..5c53b84 100644 --- a/src/java/org/apache/cassandra/cql3/WhereClause.java +++ b/src/java/org/apache/cassandra/cql3/WhereClause.java @@ -15,18 +15,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.cassandra.cql3; import java.util.List; import com.google.common.collect.ImmutableList; +import org.antlr.runtime.RecognitionException; import org.apache.cassandra.cql3.restrictions.CustomIndexExpression; +import org.apache.cassandra.schema.ColumnMetadata; + +import static java.lang.String.join; + +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.transform; public final class WhereClause { - private static final WhereClause EMPTY = new WhereClause(new Builder()); public final List<Relation> relations; @@ -34,9 +39,8 @@ public final class WhereClause private WhereClause(Builder builder) { - this.relations = builder.relations.build(); - this.expressions = builder.expressions.build(); - + relations = builder.relations.build(); + expressions = builder.expressions.build(); } public static WhereClause empty() @@ -49,6 +53,38 @@ public final class WhereClause return !expressions.isEmpty(); } + /** + * Renames identifiers in all relations + * @param from the old identifier + * @param to the new identifier + * @return a new WhereClause with with "from" replaced by "to" in all relations + */ + public WhereClause renameIdentifier(ColumnMetadata.Raw from, ColumnMetadata.Raw to) + { + WhereClause.Builder builder = new WhereClause.Builder(); + + relations.stream() + .map(r -> r.renameIdentifier(from, to)) + .forEach(builder::add); + + expressions.forEach(builder::add); + + return builder.build(); + } + + public static WhereClause parse(String cql) throws RecognitionException + { + return CQLFragmentParser.parseAnyUnhandled(CqlParser::whereClause, cql).build(); + } + + @Override + public String toString() + { + return join(" AND ", + concat(transform(relations, Relation::toString), + transform(expressions, CustomIndexExpression::toString))); + } + public static final class Builder { ImmutableList.Builder<Relation> relations = new ImmutableList.Builder<>(); http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/functions/AbstractFunction.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/functions/AbstractFunction.java b/src/java/org/apache/cassandra/cql3/functions/AbstractFunction.java index 5e10e9f..1d96d19 100644 --- a/src/java/org/apache/cassandra/cql3/functions/AbstractFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/AbstractFunction.java @@ -17,6 +17,7 @@ */ package org.apache.cassandra.cql3.functions; +import java.nio.ByteBuffer; import java.util.List; import com.google.common.base.Objects; @@ -25,6 +26,7 @@ import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.CQL3Type; import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.db.marshal.AbstractType; + import org.apache.commons.lang3.text.StrBuilder; import static java.util.stream.Collectors.toList; @@ -90,6 +92,11 @@ public abstract class AbstractFunction implements Function return false; } + public boolean referencesUserType(ByteBuffer name) + { + return false; + } + @Override public int hashCode() { http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/functions/Function.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/functions/Function.java b/src/java/org/apache/cassandra/cql3/functions/Function.java index 5d258af..5e82c04 100644 --- a/src/java/org/apache/cassandra/cql3/functions/Function.java +++ b/src/java/org/apache/cassandra/cql3/functions/Function.java @@ -17,10 +17,13 @@ */ package org.apache.cassandra.cql3.functions; +import java.nio.ByteBuffer; import java.util.List; import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.db.marshal.UserType; +import org.apache.cassandra.schema.Diff; import org.github.jamm.Unmetered; @Unmetered @@ -48,6 +51,8 @@ public interface Function extends AssignmentTestable public boolean hasReferenceTo(Function function); + public boolean referencesUserType(ByteBuffer name); + /** * Returns the name of the function to use within a ResultSet. * @@ -55,4 +60,9 @@ public interface Function extends AssignmentTestable * @return the name of the function to use within a ResultSet */ public String columnName(List<String> columnNames); + + default boolean equals(Function other, Diff.Mode mode) + { + return equals(other); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java index 1a3174c..6bc3cdf 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java @@ -21,16 +21,20 @@ import java.nio.ByteBuffer; import java.util.*; import com.google.common.base.Objects; +import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.datastax.driver.core.TypeCodec; import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.db.marshal.UserType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.schema.Functions; import org.apache.cassandra.tracing.Tracing; import org.apache.cassandra.transport.ProtocolVersion; +import static com.google.common.collect.Iterables.any; + /** * Base class for user-defined-aggregates. */ @@ -55,13 +59,13 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction super(name, argTypes, returnType); this.stateFunction = stateFunc; this.finalFunction = finalFunc; - this.stateType = stateFunc != null ? stateFunc.returnType() : null; - this.stateTypeCodec = stateType != null ? UDHelper.codecFor(UDHelper.driverType(stateType)) : null; - this.returnTypeCodec = returnType != null ? UDHelper.codecFor(UDHelper.driverType(returnType)) : null; + this.stateType = stateFunc.returnType(); + this.stateTypeCodec = UDHelper.codecFor(UDHelper.driverType(stateType)); + this.returnTypeCodec = UDHelper.codecFor(UDHelper.driverType(returnType)); this.initcond = initcond; } - public static UDAggregate create(Functions functions, + public static UDAggregate create(Collection<UDFunction> functions, FunctionName name, List<AbstractType<?>> argTypes, AbstractType<?> returnType, @@ -69,7 +73,6 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction FunctionName finalFunc, AbstractType<?> stateType, ByteBuffer initcond) - throws InvalidRequestException { List<AbstractType<?>> stateTypes = new ArrayList<>(argTypes.size() + 1); stateTypes.add(stateType); @@ -78,27 +81,17 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction return new UDAggregate(name, argTypes, returnType, - resolveScalar(functions, name, stateFunc, stateTypes), - finalFunc != null ? resolveScalar(functions, name, finalFunc, finalTypes) : null, + findFunction(functions, stateFunc, stateTypes), + null == finalFunc ? null : findFunction(functions, finalFunc, finalTypes), initcond); } - public static UDAggregate createBroken(FunctionName name, - List<AbstractType<?>> argTypes, - AbstractType<?> returnType, - ByteBuffer initcond, - InvalidRequestException reason) + private static UDFunction findFunction(Collection<UDFunction> functions, FunctionName name, List<AbstractType<?>> arguments) { - return new UDAggregate(name, argTypes, returnType, null, null, initcond) - { - public Aggregate newAggregate() throws InvalidRequestException - { - throw new InvalidRequestException(String.format("Aggregate '%s' exists but hasn't been loaded successfully for the following reason: %s. " - + "Please see the server log for more details", - this, - reason.getMessage())); - } - }; + return functions.stream() + .filter(f -> f.name().equals(name) && Functions.typesMatch(f.argTypes(), arguments)) + .findFirst() + .orElseThrow(AssertionError::new); } public boolean hasReferenceTo(Function function) @@ -107,6 +100,29 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction } @Override + public boolean referencesUserType(ByteBuffer name) + { + return any(argTypes(), t -> t.referencesUserType(name)) + || returnType.referencesUserType(name) + || (null != stateType && stateType.referencesUserType(name)) + || stateFunction.referencesUserType(name) + || (null != finalFunction && finalFunction.referencesUserType(name)); + } + + public UDAggregate withUpdatedUserType(Collection<UDFunction> udfs, UserType udt) + { + if (!referencesUserType(udt.name)) + return this; + + return new UDAggregate(name, + Lists.transform(argTypes, t -> t.withUpdatedUserType(udt)), + returnType.withUpdatedUserType(udt), + findFunction(udfs, stateFunction.name(), stateFunction.argTypes()), + null == finalFunction ? null : findFunction(udfs, finalFunction.name(), finalFunction.argTypes()), + initcond); + } + + @Override public void addFunctionsTo(List<Function> functions) { functions.add(this); @@ -214,23 +230,6 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction }; } - private static ScalarFunction resolveScalar(Functions functions, FunctionName aName, FunctionName fName, List<AbstractType<?>> argTypes) throws InvalidRequestException - { - Optional<Function> fun = functions.find(fName, argTypes); - if (!fun.isPresent()) - throw new InvalidRequestException(String.format("Referenced state function '%s %s' for aggregate '%s' does not exist", - fName, - Arrays.toString(UDHelper.driverTypes(argTypes)), - aName)); - - if (!(fun.get() instanceof ScalarFunction)) - throw new InvalidRequestException(String.format("Referenced state function '%s %s' for aggregate '%s' is not a scalar function", - fName, - Arrays.toString(UDHelper.driverTypes(argTypes)), - aName)); - return (ScalarFunction) fun.get(); - } - @Override public boolean equals(Object o) { http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/functions/UDFunction.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java index b6fedcc..3df3239 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java @@ -37,6 +37,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import com.google.common.base.Objects; +import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,6 +45,7 @@ import com.datastax.driver.core.DataType; import com.datastax.driver.core.TypeCodec; import org.apache.cassandra.config.Config; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.db.marshal.UserType; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.db.marshal.AbstractType; @@ -56,6 +58,8 @@ import org.apache.cassandra.tracing.Tracing; import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.JVMStabilityInspector; +import static com.google.common.collect.Iterables.any; + /** * Base class for User Defined Functions. */ @@ -214,6 +218,24 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct keyspaceMetadata); } + public static UDFunction tryCreate(FunctionName name, + List<ColumnIdentifier> argNames, + List<AbstractType<?>> argTypes, + AbstractType<?> returnType, + boolean calledOnNullInput, + String language, + String body) + { + try + { + return create(name, argNames, argTypes, returnType, calledOnNullInput, language, body); + } + catch (InvalidRequestException e) + { + return UDFunction.createBrokenFunction(name, argNames, argTypes, returnType, calledOnNullInput, language, body, e); + } + } + public static UDFunction create(FunctionName name, List<ColumnIdentifier> argNames, List<AbstractType<?>> argTypes, @@ -582,6 +604,26 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct } @Override + public boolean referencesUserType(ByteBuffer name) + { + return any(argTypes(), t -> t.referencesUserType(name)) || returnType.referencesUserType(name); + } + + public UDFunction withUpdatedUserType(UserType udt) + { + if (!referencesUserType(udt.name)) + return this; + + return tryCreate(name, + argNames, + Lists.transform(argTypes, t -> t.withUpdatedUserType(udt)), + returnType.withUpdatedUserType(udt), + calledOnNullInput, + language, + body); + } + + @Override public boolean equals(Object o) { if (!(o instanceof UDFunction)) http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/restrictions/CustomIndexExpression.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/CustomIndexExpression.java b/src/java/org/apache/cassandra/cql3/restrictions/CustomIndexExpression.java index 6ea6842..19090b2 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/CustomIndexExpression.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/CustomIndexExpression.java @@ -27,12 +27,12 @@ public class CustomIndexExpression { private final ColumnIdentifier valueColId = new ColumnIdentifier("custom index expression", false); - public final IndexName targetIndex; + public final QualifiedName targetIndex; public final Term.Raw valueRaw; private Term value; - public CustomIndexExpression(IndexName targetIndex, Term.Raw value) + public CustomIndexExpression(QualifiedName targetIndex, Term.Raw value) { this.targetIndex = targetIndex; this.valueRaw = value; @@ -49,8 +49,14 @@ public class CustomIndexExpression { filter.addCustomIndexExpression(table, table.indexes - .get(targetIndex.getIdx()) + .get(targetIndex.getName()) .orElseThrow(() -> IndexRestrictions.indexNotFound(targetIndex, table)), value.bindAndGet(options)); } + + @Override + public String toString() + { + return String.format("expr(%s,%s)", targetIndex, valueRaw); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/restrictions/IndexRestrictions.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/IndexRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/IndexRestrictions.java index ced04ed..91038fb 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/IndexRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/IndexRestrictions.java @@ -21,8 +21,8 @@ package org.apache.cassandra.cql3.restrictions; import java.util.ArrayList; import java.util.List; +import org.apache.cassandra.cql3.QualifiedName; import org.apache.cassandra.schema.TableMetadata; -import org.apache.cassandra.cql3.IndexName; import org.apache.cassandra.exceptions.InvalidRequestException; public class IndexRestrictions @@ -61,23 +61,23 @@ public class IndexRestrictions return customExpressions; } - static InvalidRequestException invalidIndex(IndexName indexName, TableMetadata table) + static InvalidRequestException invalidIndex(QualifiedName indexName, TableMetadata table) { - return new InvalidRequestException(String.format(INVALID_INDEX, indexName.getIdx(), table.toString())); + return new InvalidRequestException(String.format(INVALID_INDEX, indexName.getName(), table)); } - static InvalidRequestException indexNotFound(IndexName indexName, TableMetadata table) + static InvalidRequestException indexNotFound(QualifiedName indexName, TableMetadata table) { - return new InvalidRequestException(String.format(INDEX_NOT_FOUND, indexName.getIdx(), table.toString())); + return new InvalidRequestException(String.format(INDEX_NOT_FOUND, indexName.getName(), table)); } - static InvalidRequestException nonCustomIndexInExpression(IndexName indexName) + static InvalidRequestException nonCustomIndexInExpression(QualifiedName indexName) { - return new InvalidRequestException(String.format(NON_CUSTOM_INDEX_IN_EXPRESSION, indexName.getIdx())); + return new InvalidRequestException(String.format(NON_CUSTOM_INDEX_IN_EXPRESSION, indexName.getName())); } - static InvalidRequestException customExpressionNotSupported(IndexName indexName) + static InvalidRequestException customExpressionNotSupported(QualifiedName indexName) { - return new InvalidRequestException(String.format(CUSTOM_EXPRESSION_NOT_SUPPORTED, indexName.getIdx())); + return new InvalidRequestException(String.format(CUSTOM_EXPRESSION_NOT_SUPPORTED, indexName.getName())); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java index b368c22..8fb02fe 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java @@ -129,12 +129,27 @@ public final class StatementRestrictions boolean allowFiltering, boolean forView) { + this(type, table, whereClause, boundNames, selectsOnlyStaticColumns, type.allowUseOfSecondaryIndices(), allowFiltering, forView); + } + + + // allowUseOfSecondaryIndices override for CREATE MV statement restriction processing purposes (we do not want + // the statement to trigger Keyspace.open to determine the value) + public StatementRestrictions(StatementType type, + TableMetadata table, + WhereClause whereClause, + VariableSpecifications boundNames, + boolean selectsOnlyStaticColumns, + boolean allowUseOfSecondaryIndices, + boolean allowFiltering, + boolean forView) + { this(type, table, allowFiltering); ColumnFamilyStore cfs; SecondaryIndexManager secondaryIndexManager = null; - if (type.allowUseOfSecondaryIndices()) + if (allowUseOfSecondaryIndices) { cfs = Keyspace.open(table.keyspace).getColumnFamilyStore(table.name); secondaryIndexManager = cfs.indexManager; @@ -156,14 +171,13 @@ public final class StatementRestrictions if (!forView) throw new InvalidRequestException("Unsupported restriction: " + relation); - for (ColumnMetadata def : relation.toRestriction(table, boundNames).getColumnDefs()) - this.notNullColumns.add(def); + this.notNullColumns.addAll(relation.toRestriction(table, boundNames).getColumnDefs()); } else if (relation.isLIKE()) { Restriction restriction = relation.toRestriction(table, boundNames); - if (!type.allowUseOfSecondaryIndices() || !restriction.hasSupportingIndex(secondaryIndexManager)) + if (!allowUseOfSecondaryIndices || !restriction.hasSupportingIndex(secondaryIndexManager)) throw new InvalidRequestException(String.format("LIKE restriction is only supported on properly " + "indexed columns. %s is not valid.", relation.toString())); @@ -181,7 +195,7 @@ public final class StatementRestrictions boolean hasQueriableClusteringColumnIndex = false; boolean hasQueriableIndex = false; - if (type.allowUseOfSecondaryIndices()) + if (allowUseOfSecondaryIndices) { if (whereClause.containsCustomExpressions()) processCustomIndexExpressions(whereClause.expressions, boundNames, secondaryIndexManager); @@ -569,18 +583,15 @@ public final class StatementRestrictions CustomIndexExpression expression = expressions.get(0); - CFName cfName = expression.targetIndex.getCfName(); - if (cfName.hasKeyspace() - && !expression.targetIndex.getKeyspace().equals(table.keyspace)) - throw IndexRestrictions.invalidIndex(expression.targetIndex, table); + QualifiedName name = expression.targetIndex; - if (cfName.getColumnFamily() != null && !cfName.getColumnFamily().equals(table.name)) + if (name.hasKeyspace() && !name.getKeyspace().equals(table.keyspace)) throw IndexRestrictions.invalidIndex(expression.targetIndex, table); - if (!table.indexes.has(expression.targetIndex.getIdx())) + if (!table.indexes.has(expression.targetIndex.getName())) throw IndexRestrictions.indexNotFound(expression.targetIndex, table); - Index index = indexManager.getIndex(table.indexes.get(expression.targetIndex.getIdx()).get()); + Index index = indexManager.getIndex(table.indexes.get(expression.targetIndex.getName()).get()); if (!index.getIndexMetadata().isCustom()) throw IndexRestrictions.nonCustomIndexInExpression(expression.targetIndex); http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java deleted file mode 100644 index 0de5b2a..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java +++ /dev/null @@ -1,112 +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.auth.Permission; -import org.apache.cassandra.config.DatabaseDescriptor; -import org.apache.cassandra.exceptions.*; -import org.apache.cassandra.locator.AbstractReplicationStrategy; -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.ClientState; -import org.apache.cassandra.service.ClientWarn; -import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.service.StorageService; -import org.apache.cassandra.transport.Event; - -public class AlterKeyspaceStatement extends SchemaAlteringStatement -{ - private final String name; - private final KeyspaceAttributes attrs; - - public AlterKeyspaceStatement(String name, KeyspaceAttributes attrs) - { - super(); - this.name = name; - this.attrs = attrs; - } - - @Override - public String keyspace() - { - return name; - } - - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException - { - state.hasKeyspaceAccess(name, Permission.ALTER); - } - - public void validate(ClientState state) throws RequestValidationException - { - KeyspaceMetadata ksm = Schema.instance.getKeyspaceMetadata(name); - if (ksm == null) - throw new InvalidRequestException("Unknown keyspace " + name); - if (SchemaConstants.isSystemKeyspace(ksm.name)) - throw new InvalidRequestException("Cannot alter system keyspace"); - - attrs.validate(); - - if (attrs.getReplicationStrategyClass() == null && !attrs.getReplicationOptions().isEmpty()) - throw new ConfigurationException("Missing replication strategy class"); - - if (attrs.getReplicationStrategyClass() != null) - { - // The strategy is validated through KSMetaData.validate() in announceKeyspaceUpdate below. - // However, for backward compatibility with thrift, this doesn't validate unexpected options yet, - // so doing proper validation here. - KeyspaceParams params = attrs.asAlteredKeyspaceParams(ksm.params); - 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."); - warnIfIncreasingRF(ksm, params); - } - } - - private void warnIfIncreasingRF(KeyspaceMetadata ksm, KeyspaceParams params) - { - AbstractReplicationStrategy oldStrategy = AbstractReplicationStrategy.createReplicationStrategy(ksm.name, - ksm.params.replication.klass, - StorageService.instance.getTokenMetadata(), - DatabaseDescriptor.getEndpointSnitch(), - ksm.params.replication.options); - AbstractReplicationStrategy newStrategy = AbstractReplicationStrategy.createReplicationStrategy(keyspace(), - params.replication.klass, - StorageService.instance.getTokenMetadata(), - DatabaseDescriptor.getEndpointSnitch(), - params.replication.options); - if (newStrategy.getReplicationFactor() > oldStrategy.getReplicationFactor()) - ClientWarn.instance.warn("When increasing replication factor you need to run a full (-full) repair to distribute the data."); - } - - public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException - { - KeyspaceMetadata oldKsm = Schema.instance.getKeyspaceMetadata(name); - // In the (very) unlikely case the keyspace was dropped since validate() - if (oldKsm == null) - throw new InvalidRequestException("Unknown keyspace " + name); - - KeyspaceMetadata newKsm = oldKsm.withSwapped(attrs.asAlteredKeyspaceParams(oldKsm.params)); - MigrationManager.announceKeyspaceUpdate(newKsm, isLocalOnly); - return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, keyspace()); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/src/java/org/apache/cassandra/cql3/statements/AlterRoleStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterRoleStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterRoleStatement.java index 6134741..6572267 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AlterRoleStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AlterRoleStatement.java @@ -43,13 +43,13 @@ public class AlterRoleStatement extends AuthenticationStatement if (opts.isEmpty()) throw new InvalidRequestException("ALTER [ROLE|USER] can't be empty"); - // validate login here before checkAccess to avoid leaking user existence to anonymous users. + // validate login here before authorize to avoid leaking user existence to anonymous users. state.ensureNotAnonymous(); if (!DatabaseDescriptor.getRoleManager().isExistingRole(role)) throw new InvalidRequestException(String.format("%s doesn't exist", role.getRoleName())); } - public void checkAccess(ClientState state) throws UnauthorizedException + public void authorize(ClientState state) throws UnauthorizedException { AuthenticatedUser user = state.getUser(); boolean isSuper = user.isSuper(); http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/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 index 35459de..22f781a 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java @@ -56,7 +56,7 @@ public class AlterTableStatement extends SchemaAlteringStatement private final List<AlterTableStatementColumn> colNameList; private final Long deleteTimestamp; - public AlterTableStatement(CFName name, + public AlterTableStatement(QualifiedName name, Type type, List<AlterTableStatementColumn> colDataList, TableAttributes attrs, @@ -71,9 +71,9 @@ public class AlterTableStatement extends SchemaAlteringStatement this.deleteTimestamp = deleteTimestamp; } - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException + public void authorize(ClientState state) throws UnauthorizedException, InvalidRequestException { - state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.ALTER); + state.ensureTablePermission(keyspace(), name(), Permission.ALTER); } public void validate(ClientState state) @@ -83,7 +83,7 @@ public class AlterTableStatement extends SchemaAlteringStatement public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException { - TableMetadata current = Schema.instance.validateTable(keyspace(), columnFamily()); + TableMetadata current = Schema.instance.validateTable(keyspace(), name()); if (current.isView()) throw new InvalidRequestException("Cannot use ALTER TABLE on Materialized View"); @@ -96,7 +96,7 @@ public class AlterTableStatement extends SchemaAlteringStatement CQL3Type validator = null; List<ViewMetadata> viewUpdates = new ArrayList<>(); - Iterable<ViewMetadata> views = View.findAll(keyspace(), columnFamily()); + Iterable<ViewMetadata> views = View.findAll(keyspace(), name()); switch (oType) { @@ -187,7 +187,7 @@ public class AlterTableStatement extends SchemaAlteringStatement def = builder.getColumn(columnName); if (def == null) - throw new InvalidRequestException(String.format("Column %s was not found in table %s", columnName, columnFamily())); + throw new InvalidRequestException(String.format("Column %s was not found in table %s", columnName, name())); switch (def.kind) { @@ -228,7 +228,7 @@ public class AlterTableStatement extends SchemaAlteringStatement if (rejectAlter) viewNames.append(','); rejectAlter = true; - viewNames.append(view.name); + viewNames.append(view.name()); } if (rejectAlter) throw new InvalidRequestException(String.format("Cannot drop column %s, depended on by materialized views (%s.{%s})", @@ -298,7 +298,7 @@ public class AlterTableStatement extends SchemaAlteringStatement ColumnIdentifier viewFrom = entry.getKey().getIdentifier(view.metadata); ColumnIdentifier viewTo = entry.getValue().getIdentifier(view.metadata); - viewUpdates.add(view.renamePrimaryKeyColumn(viewFrom, viewTo)); + viewUpdates.add(view.withRenamedPrimaryKeyColumn(viewFrom, viewTo)); } } break; @@ -309,14 +309,14 @@ public class AlterTableStatement extends SchemaAlteringStatement for (ViewMetadata viewUpdate : viewUpdates) MigrationManager.announceViewUpdate(viewUpdate, isLocalOnly); - return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily()); + return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), name()); } @Override public String toString() { return String.format("AlterTableStatement(name=%s, type=%s)", - cfName, + qualifiedName, oType); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/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 index 614b482..2cff49c 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java @@ -30,6 +30,8 @@ import org.apache.cassandra.service.ClientState; import org.apache.cassandra.service.QueryState; import org.apache.cassandra.transport.Event; +import static java.util.function.Predicate.isEqual; + public abstract class AlterTypeStatement extends SchemaAlteringStatement { protected final UTName name; @@ -40,7 +42,7 @@ public abstract class AlterTypeStatement extends SchemaAlteringStatement } @Override - public void prepareKeyspace(ClientState state) throws InvalidRequestException + public void setKeyspace(ClientState state) throws InvalidRequestException { if (!name.hasKeyspace()) name.setKeyspace(state.getKeyspace()); @@ -66,9 +68,9 @@ public abstract class AlterTypeStatement extends SchemaAlteringStatement return new Renames(name, renames); } - public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException + public void authorize(ClientState state) throws UnauthorizedException, InvalidRequestException { - state.hasKeyspaceAccess(keyspace(), Permission.ALTER); + state.ensureKeyspacePermission(keyspace(), Permission.ALTER); } public void validate(ClientState state) throws RequestValidationException @@ -104,7 +106,7 @@ public abstract class AlterTypeStatement extends SchemaAlteringStatement protected void checkTypeNotUsedByAggregate(KeyspaceMetadata ksm) { - ksm.functions.udas().filter(aggregate -> aggregate.initialCondition() != null && aggregate.stateType().referencesUserType(name.getStringTypeName())) + ksm.functions.udas().filter(aggregate -> aggregate.initialCondition() != null && aggregate.stateType().referencesUserType(name.getUserTypeName())) .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)); @@ -133,7 +135,7 @@ public abstract class AlterTypeStatement extends SchemaAlteringStatement newNames.add(fieldName); AbstractType<?> addType = type.prepare(keyspace()).getType(); - if (addType.referencesUserType(toUpdate.getNameAsString())) + if (addType.referencesUserType(toUpdate.name)) 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); @@ -172,7 +174,15 @@ public abstract class AlterTypeStatement extends SchemaAlteringStatement } UserType updated = new UserType(toUpdate.keyspace, toUpdate.name, newNames, newTypes, toUpdate.isMultiCell()); - CreateTypeStatement.checkForDuplicateNames(updated); + + List<FieldIdentifier> fieldNames = new ArrayList<>(updated.fieldNames()); + + fieldNames.forEach(name -> + { + if (fieldNames.stream().filter(isEqual(name)).count() > 1) + throw new InvalidRequestException(String.format("Duplicate field name %s in type %s", name, updated.getNameAsString())); + }); + return updated; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/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 fbfc54c..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/AlterViewStatement.java +++ /dev/null @@ -1,96 +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.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); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/aaddbd49/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/aaddbd49/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 83081c8..50ca005 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; @@ -29,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 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) @@ -50,9 +43,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/aaddbd49/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 e181968..b5976a3 100644 --- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java @@ -44,6 +44,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; /** @@ -56,8 +58,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) @@ -90,10 +92,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; @@ -122,6 +124,26 @@ public class BatchStatement implements CQLStatement this.hasConditions = hasConditions; } + @Override + public List<ColumnSpecification> getBindVariables() + { + return bindVariables.getSpecifications(); + } + + @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.getPartitionKeyBindIndexes(statements.get(0).metadata()); + } + + @Override public Iterable<org.apache.cassandra.cql3.functions.Function> getFunctions() { List<org.apache.cassandra.cql3.functions.Function> functions = new ArrayList<>(); @@ -130,15 +152,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. @@ -458,7 +475,7 @@ public class BatchStatement implements CQLStatement return Pair.create(casRequest, columnsWithConditions); } - public ResultMessage executeInternal(QueryState queryState, QueryOptions options) throws RequestValidationException, RequestExecutionException + public ResultMessage executeLocally(QueryState queryState, QueryOptions options) throws RequestValidationException, RequestExecutionException { if (hasConditions) return executeInternalWithConditions(BatchQueryOptions.withoutPerStatementVariables(options), queryState); @@ -494,7 +511,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; @@ -509,48 +526,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/aaddbd49/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 136860e..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/CFStatement.java +++ /dev/null @@ -1,66 +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; - -/** - * 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 be prepared correctly"; - return cfName.getKeyspace(); - } - - public String columnFamily() - { - return cfName.getColumnFamily(); - } -} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org