http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/QualifiedName.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/QualifiedName.java b/src/java/org/apache/cassandra/cql3/QualifiedName.java new file mode 100644 index 0000000..fb2e110 --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/QualifiedName.java @@ -0,0 +1,116 @@ +/* + * 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; + +import java.util.Locale; +import java.util.Objects; + +/** + * Class for the names of the keyspace-prefixed elements (e.g. table, index, view names) + */ +public class QualifiedName +{ + /** + * The keyspace name as stored internally. + */ + private String keyspace; + private String name; + + public QualifiedName() + { + } + + public QualifiedName(String keyspace, String name) + { + this.keyspace = keyspace; + this.name = name; + } + + /** + * Sets the keyspace. + * + * @param ks the keyspace name + * @param keepCase <code>true</code> if the case must be kept, <code>false</code> otherwise. + */ + public final void setKeyspace(String ks, boolean keepCase) + { + keyspace = toInternalName(ks, keepCase); + } + + /** + * Checks if the keyspace is specified. + * @return <code>true</code> if the keyspace is specified, <code>false</code> otherwise. + */ + public final boolean hasKeyspace() + { + return keyspace != null; + } + + public final String getKeyspace() + { + return keyspace; + } + + public void setName(String cf, boolean keepCase) + { + name = toInternalName(cf, keepCase); + } + + public String getName() + { + return name; + } + + @Override + public String toString() + { + return hasKeyspace() + ? String.format("%s.%s", keyspace, name) + : name; + } + + @Override + public int hashCode() + { + return Objects.hash(keyspace, name); + } + + public boolean equals(Object o) + { + if (this == o) + return true; + + if (!(o instanceof QualifiedName)) + return false; + + QualifiedName qn = (QualifiedName) o; + return Objects.equals(keyspace, qn.keyspace) && name.equals(qn.name); + } + + /** + * Converts the specified name into the name used internally. + * + * @param name the name + * @param keepCase <code>true</code> if the case must be kept, <code>false</code> otherwise. + * @return the name used internally. + */ + private static String toInternalName(String name, boolean keepCase) + { + return keepCase ? name : name.toLowerCase(Locale.US); + } +}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/QueryHandler.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/QueryHandler.java b/src/java/org/apache/cassandra/cql3/QueryHandler.java index d3b41f0..b21f9e3 100644 --- a/src/java/org/apache/cassandra/cql3/QueryHandler.java +++ b/src/java/org/apache/cassandra/cql3/QueryHandler.java @@ -21,7 +21,6 @@ import java.nio.ByteBuffer; import java.util.Map; import org.apache.cassandra.cql3.statements.BatchStatement; -import org.apache.cassandra.cql3.statements.ParsedStatement; import org.apache.cassandra.exceptions.RequestExecutionException; import org.apache.cassandra.exceptions.RequestValidationException; import org.apache.cassandra.service.ClientState; @@ -41,7 +40,7 @@ public interface QueryHandler ClientState clientState, Map<String, ByteBuffer> customPayload) throws RequestValidationException; - ParsedStatement.Prepared getPrepared(MD5Digest id); + QueryHandler.Prepared getPrepared(MD5Digest id); ResultMessage processPrepared(CQLStatement statement, QueryState state, @@ -54,4 +53,30 @@ public interface QueryHandler BatchQueryOptions options, Map<String, ByteBuffer> customPayload, long queryStartNanoTime) throws RequestExecutionException, RequestValidationException; + + public static class Prepared + { + public final CQLStatement statement; + + public final MD5Digest resultMetadataId; + + /** + * Contains the CQL statement source if the statement has been "regularly" perpared via + * {@link QueryHandler#prepare(String, ClientState, Map)}. + * Other usages of this class may or may not contain the CQL statement source. + */ + public final String rawCQLStatement; + + public Prepared(CQLStatement statement) + { + this(statement, ""); + } + + public Prepared(CQLStatement statement, String rawCQLStatement) + { + this.statement = statement; + this.rawCQLStatement = rawCQLStatement; + this.resultMetadataId = ResultSet.ResultMetadata.fromPrepared(statement).getResultMetadataId(); + } + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/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 3f0b196..77b4cdc 100644 --- a/src/java/org/apache/cassandra/cql3/QueryProcessor.java +++ b/src/java/org/apache/cassandra/cql3/QueryProcessor.java @@ -67,11 +67,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 @@ -118,7 +118,7 @@ public class QueryProcessor implements QueryHandler } // Work around initialization dependency - private static enum InternalStateInstance + private enum InternalStateInstance { INSTANCE; @@ -126,9 +126,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)); } } @@ -174,7 +172,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); } @@ -201,7 +199,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); @@ -226,10 +224,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) @@ -238,7 +235,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); } @@ -257,43 +254,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 @@ -311,8 +310,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 @@ -326,24 +325,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 @@ -351,16 +350,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); } @@ -374,7 +373,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); } @@ -393,12 +392,12 @@ 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(); return storePreparedStatement(queryString, clientState.getRawKeyspace(), prepared); } @@ -413,19 +412,19 @@ 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)); - ResultSet.PreparedMetadata preparedMetadata = ResultSet.PreparedMetadata.fromPrepared(existing); - ResultSet.ResultMetadata resultMetadata = ResultSet.ResultMetadata.fromPrepared(existing); + ResultSet.PreparedMetadata preparedMetadata = ResultSet.PreparedMetadata.fromPrepared(existing.statement); + ResultSet.ResultMetadata resultMetadata = ResultSet.ResultMetadata.fromPrepared(existing.statement); return new ResultMessage.Prepared(statementId, resultMetadata.getResultMetadataId(), preparedMetadata, resultMetadata); } - 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). @@ -440,8 +439,8 @@ public class QueryProcessor implements QueryHandler MD5Digest statementId = computeId(queryString, keyspace); preparedStatements.put(statementId, prepared); SystemKeyspace.writePreparedStatement(keyspace, statementId, queryString); - ResultSet.PreparedMetadata preparedMetadata = ResultSet.PreparedMetadata.fromPrepared(prepared); - ResultSet.ResultMetadata resultMetadata = ResultSet.ResultMetadata.fromPrepared(prepared); + ResultSet.PreparedMetadata preparedMetadata = ResultSet.PreparedMetadata.fromPrepared(prepared.statement); + ResultSet.ResultMetadata resultMetadata = ResultSet.ResultMetadata.fromPrepared(prepared.statement); return new ResultMessage.Prepared(statementId, resultMetadata.getResultMetadataId(), preparedMetadata, resultMetadata); } @@ -460,11 +459,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 @@ -491,31 +490,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()); @@ -527,7 +526,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 { @@ -551,7 +550,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)); } @@ -577,10 +576,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()); @@ -593,12 +592,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()); @@ -607,7 +606,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/207c80c1/src/java/org/apache/cassandra/cql3/ResultSet.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/ResultSet.java b/src/java/org/apache/cassandra/cql3/ResultSet.java index 9d79dea..455f7c4 100644 --- a/src/java/org/apache/cassandra/cql3/ResultSet.java +++ b/src/java/org/apache/cassandra/cql3/ResultSet.java @@ -31,7 +31,6 @@ import java.util.Objects; import com.google.common.annotations.VisibleForTesting; import io.netty.buffer.ByteBuf; -import org.apache.cassandra.cql3.statements.ParsedStatement; import org.apache.cassandra.cql3.statements.SelectStatement; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.service.pager.PagingState; @@ -306,10 +305,8 @@ public class ResultSet return resultMetadataId; } - public static ResultMetadata fromPrepared(ParsedStatement.Prepared prepared) + public static ResultMetadata fromPrepared(CQLStatement statement) { - CQLStatement statement = prepared.statement; - if (statement instanceof SelectStatement) return ((SelectStatement)statement).getResultMetadata(); @@ -569,9 +566,9 @@ public class ResultSet return sb.toString(); } - public static PreparedMetadata fromPrepared(ParsedStatement.Prepared prepared) + public static PreparedMetadata fromPrepared(CQLStatement statement) { - return new PreparedMetadata(prepared.boundNames, prepared.partitionKeyBindIndexes); + return new PreparedMetadata(statement.getBindVariables(), statement.getPartitionKeyBindVariableIndexes()); } private static class Codec implements CBCodec<PreparedMetadata> http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/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..d9c5b26 100644 --- a/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java +++ b/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cql3; import java.util.Collections; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.TableMetadata; @@ -149,12 +150,34 @@ 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); } @Override + public int hashCode() + { + return Objects.hash(relationType, entity, mapKey, value, inValues); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + return true; + + if (!(o instanceof SingleColumnRelation)) + return false; + + SingleColumnRelation scr = (SingleColumnRelation) o; + return Objects.equals(entity, scr.entity) + && Objects.equals(mapKey, scr.mapKey) + && Objects.equals(value, scr.value) + && Objects.equals(inValues, scr.inValues); + } + + @Override protected Restriction newEQRestriction(TableMetadata table, VariableSpecifications boundNames) { ColumnMetadata columnDef = entity.prepare(table); http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/Term.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Term.java b/src/java/org/apache/cassandra/cql3/Term.java index f1737d1..f536baa 100644 --- a/src/java/org/apache/cassandra/cql3/Term.java +++ b/src/java/org/apache/cassandra/cql3/Term.java @@ -124,6 +124,18 @@ public interface Term { return getText(); } + + @Override + public int hashCode() + { + return getText().hashCode(); + } + + @Override + public boolean equals(Object o) + { + return this == o || (o instanceof Raw && getText().equals(((Raw) o).getText())); + } } public abstract class MultiColumnRaw extends Term.Raw http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/TokenRelation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/TokenRelation.java b/src/java/org/apache/cassandra/cql3/TokenRelation.java index 62f603b..4e3313d 100644 --- a/src/java/org/apache/cassandra/cql3/TokenRelation.java +++ b/src/java/org/apache/cassandra/cql3/TokenRelation.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cql3; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import com.google.common.base.Joiner; @@ -143,6 +144,25 @@ public final class TokenRelation extends Relation return String.format("token%s %s %s", Tuples.tupleToString(entities), relationType, value); } + @Override + public int hashCode() + { + return Objects.hash(relationType, entities, value); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + return true; + + if (!(o instanceof TokenRelation)) + return false; + + TokenRelation tr = (TokenRelation) o; + return entities.equals(tr.entities) && value.equals(tr.value); + } + /** * Returns the definition of the columns to which apply the token restriction. * http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/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..e58290e 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() + public List<ColumnSpecification> getBindVariables() { - return Arrays.asList(specs); + return specs; } /** @@ -63,7 +63,7 @@ public class VariableSpecifications * * Callers of this method should ensure that all statements operate on the same table. */ - public short[] getPartitionKeyBindIndexes(TableMetadata metadata) + public short[] getPartitionKeyBindVariableIndexes(TableMetadata metadata) { short[] partitionKeyPositions = new short[metadata.partitionKeyColumns().size()]; boolean[] set = new boolean[partitionKeyPositions.length]; @@ -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/207c80c1/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 fb783a5..87041f9 100644 --- a/src/java/org/apache/cassandra/cql3/WhereClause.java +++ b/src/java/org/apache/cassandra/cql3/WhereClause.java @@ -15,20 +15,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.cassandra.cql3; import java.util.List; +import java.util.Objects; import com.google.common.collect.ImmutableList; +import org.antlr.runtime.RecognitionException; +import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.cql3.restrictions.CustomIndexExpression; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; + +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; @@ -36,9 +40,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() @@ -51,6 +54,57 @@ 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))); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + return true; + + if (!(o instanceof WhereClause)) + return false; + + WhereClause wc = (WhereClause) o; + return relations.equals(wc.relations) && expressions.equals(wc.expressions); + } + + @Override + public int hashCode() + { + return Objects.hash(relations, expressions); + } + public static final class Builder { ImmutableList.Builder<Relation> relations = new ImmutableList.Builder<>(); @@ -73,10 +127,4 @@ public final class WhereClause return new WhereClause(this); } } - - @Override - public String toString() - { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); - } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/src/java/org/apache/cassandra/cql3/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..aea0d01 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; @@ -85,7 +87,7 @@ public abstract class AbstractFunction implements Function functions.add(this); } - public boolean hasReferenceTo(Function function) + public boolean referencesUserType(ByteBuffer name) { return false; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/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..e13e906 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 java.util.Optional; import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.schema.Difference; import org.github.jamm.Unmetered; @Unmetered @@ -46,7 +49,7 @@ public interface Function extends AssignmentTestable public void addFunctionsTo(List<Function> functions); - public boolean hasReferenceTo(Function function); + public boolean referencesUserType(ByteBuffer name); /** * Returns the name of the function to use within a ResultSet. @@ -55,4 +58,9 @@ public interface Function extends AssignmentTestable * @return the name of the function to use within a ResultSet */ public String columnName(List<String> columnNames); + + public default Optional<Difference> compare(Function other) + { + throw new UnsupportedOperationException(); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/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..1a49b33 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java @@ -21,16 +21,23 @@ 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.ConfigurationException; import org.apache.cassandra.exceptions.InvalidRequestException; +import org.apache.cassandra.schema.Difference; 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; +import static com.google.common.collect.Iterables.transform; + /** * Base class for user-defined-aggregates. */ @@ -55,13 +62,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 +76,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 +84,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(name, functions, stateFunc, stateTypes), + null == finalFunc ? null : findFunction(name, functions, finalFunc, finalTypes), initcond); } - public static UDAggregate createBroken(FunctionName name, - List<AbstractType<?>> argTypes, - AbstractType<?> returnType, - ByteBuffer initcond, - InvalidRequestException reason) + private static UDFunction findFunction(FunctionName udaName, 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(() -> new ConfigurationException(String.format("Unable to find function %s referenced by UDA %s", name, udaName))); } public boolean hasReferenceTo(Function function) @@ -107,16 +103,37 @@ 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.newArrayList(transform(argTypes, t -> t.withUpdatedUserType(udt))), + returnType.withUpdatedUserType(udt), + findFunction(name, udfs, stateFunction.name(), stateFunction.argTypes()), + null == finalFunction ? null : findFunction(name, udfs, finalFunction.name(), finalFunction.argTypes()), + initcond); + } + + @Override public void addFunctionsTo(List<Function> functions) { functions.add(this); - if (stateFunction != null) - { - stateFunction.addFunctionsTo(functions); - if (finalFunction != null) - finalFunction.addFunctionsTo(functions); - } + stateFunction.addFunctionsTo(functions); + + if (finalFunction != null) + finalFunction.addFunctionsTo(functions); } public boolean isAggregate() @@ -214,23 +231,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) { @@ -238,13 +238,83 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction return false; UDAggregate that = (UDAggregate) o; - return Objects.equal(name, that.name) - && Functions.typesMatch(argTypes, that.argTypes) - && Functions.typesMatch(returnType, that.returnType) + return equalsWithoutTypesAndFunctions(that) + && argTypes.equals(that.argTypes) + && returnType.equals(that.returnType) && Objects.equal(stateFunction, that.stateFunction) && Objects.equal(finalFunction, that.finalFunction) - && ((stateType == that.stateType) || ((stateType != null) && stateType.equals(that.stateType, true))) // ignore freezing - && Objects.equal(initcond, that.initcond); + && ((stateType == that.stateType) || ((stateType != null) && stateType.equals(that.stateType))); + } + + private boolean equalsWithoutTypesAndFunctions(UDAggregate other) + { + return name.equals(other.name) + && argTypes.size() == other.argTypes.size() + && Objects.equal(initcond, other.initcond); + } + + @Override + public Optional<Difference> compare(Function function) + { + if (!(function instanceof UDAggregate)) + throw new IllegalArgumentException(); + + UDAggregate other = (UDAggregate) function; + + if (!equalsWithoutTypesAndFunctions(other) + || ((null == finalFunction) != (null == other.finalFunction)) + || ((null == stateType) != (null == other.stateType))) + return Optional.of(Difference.SHALLOW); + + boolean differsDeeply = false; + + if (null != finalFunction && !finalFunction.equals(other.finalFunction)) + { + if (finalFunction.name().equals(other.finalFunction.name())) + differsDeeply = true; + else + return Optional.of(Difference.SHALLOW); + } + + if (null != stateType && !stateType.equals(other.stateType)) + { + if (stateType.asCQL3Type().toString().equals(other.stateType.asCQL3Type().toString())) + differsDeeply = true; + else + return Optional.of(Difference.SHALLOW); + } + + if (!returnType.equals(other.returnType)) + { + if (returnType.asCQL3Type().toString().equals(other.returnType.asCQL3Type().toString())) + differsDeeply = true; + else + return Optional.of(Difference.SHALLOW); + } + + for (int i = 0; i < argTypes().size(); i++) + { + AbstractType<?> thisType = argTypes.get(i); + AbstractType<?> thatType = other.argTypes.get(i); + + if (!thisType.equals(thatType)) + { + if (thisType.asCQL3Type().toString().equals(thatType.asCQL3Type().toString())) + differsDeeply = true; + else + return Optional.of(Difference.SHALLOW); + } + } + + if (!stateFunction.equals(other.stateFunction)) + { + if (stateFunction.name().equals(other.stateFunction.name())) + differsDeeply = true; + else + return Optional.of(Difference.SHALLOW); + } + + return differsDeeply ? Optional.of(Difference.DEEP) : Optional.empty(); } @Override http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/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..8c5a07e 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -37,6 +38,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 +46,8 @@ 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.Difference; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.db.marshal.AbstractType; @@ -56,6 +60,9 @@ 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; +import static com.google.common.collect.Iterables.transform; + /** * Base class for User Defined Functions. */ @@ -214,6 +221,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 createBrokenFunction(name, argNames, argTypes, returnType, calledOnNullInput, language, body, e); + } + } + public static UDFunction create(FunctionName name, List<ColumnIdentifier> argNames, List<AbstractType<?>> argTypes, @@ -222,7 +247,7 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct String language, String body) { - UDFunction.assertUdfsEnabled(language); + assertUdfsEnabled(language); switch (language) { @@ -399,7 +424,7 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct } /** - * Like {@link #executeAsync(int, List)} but the first parameter is already in non-serialized form. + * Like {@link #executeAsync(ProtocolVersion, List)} but the first parameter is already in non-serialized form. * Remaining parameters (2nd paramters and all others) are in {@code parameters}. * This is used to prevent superfluous (de)serialization of the state of aggregates. * Means: scalar functions of aggregates are called using this variant. @@ -582,18 +607,83 @@ 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.newArrayList(transform(argTypes, t -> t.withUpdatedUserType(udt))), + returnType.withUpdatedUserType(udt), + calledOnNullInput, + language, + body); + } + + @Override public boolean equals(Object o) { if (!(o instanceof UDFunction)) return false; UDFunction that = (UDFunction)o; - return Objects.equal(name, that.name) - && Objects.equal(argNames, that.argNames) - && Functions.typesMatch(argTypes, that.argTypes) - && Functions.typesMatch(returnType, that.returnType) - && Objects.equal(language, that.language) - && Objects.equal(body, that.body); + return equalsWithoutTypes(that) + && argTypes.equals(that.argTypes) + && returnType.equals(that.returnType); + } + + private boolean equalsWithoutTypes(UDFunction other) + { + return name.equals(other.name) + && argTypes.size() == other.argTypes.size() + && argNames.equals(other.argNames) + && body.equals(other.body) + && language.equals(other.language) + && calledOnNullInput == other.calledOnNullInput; + } + + @Override + public Optional<Difference> compare(Function function) + { + if (!(function instanceof UDFunction)) + throw new IllegalArgumentException(); + + UDFunction other = (UDFunction) function; + + if (!equalsWithoutTypes(other)) + return Optional.of(Difference.SHALLOW); + + boolean typesDifferDeeply = false; + + if (!returnType.equals(other.returnType)) + { + if (returnType.asCQL3Type().toString().equals(other.returnType.asCQL3Type().toString())) + typesDifferDeeply = true; + else + return Optional.of(Difference.SHALLOW); + } + + for (int i = 0; i < argTypes().size(); i++) + { + AbstractType<?> thisType = argTypes.get(i); + AbstractType<?> thatType = other.argTypes.get(i); + + if (!thisType.equals(thatType)) + { + if (thisType.asCQL3Type().toString().equals(thatType.asCQL3Type().toString())) + typesDifferDeeply = true; + else + return Optional.of(Difference.SHALLOW); + } + } + + return typesDifferDeeply ? Optional.of(Difference.DEEP) : Optional.empty(); } @Override http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/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 8048862..539715c 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/CustomIndexExpression.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/CustomIndexExpression.java @@ -15,26 +15,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.cassandra.cql3.restrictions; -import org.apache.cassandra.schema.TableMetadata; +import java.util.Objects; + import org.apache.cassandra.cql3.*; import org.apache.cassandra.db.filter.RowFilter; import org.apache.cassandra.db.marshal.AbstractType; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.cassandra.schema.TableMetadata; 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; @@ -51,14 +50,33 @@ 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 ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); + return String.format("expr(%s,%s)", targetIndex, valueRaw); + } + + @Override + public int hashCode() + { + return Objects.hash(targetIndex, valueRaw); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + return true; + + if (!(o instanceof CustomIndexExpression)) + return false; + + CustomIndexExpression cie = (CustomIndexExpression) o; + return targetIndex.equals(cie.targetIndex) && valueRaw.equals(cie.valueRaw); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/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 ac2e58a..fd89d1b 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; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -63,24 +63,24 @@ 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())); } @Override http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/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 af1a964..5a41da0 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java @@ -131,6 +131,22 @@ public final class StatementRestrictions boolean allowFiltering, boolean forView) { + this(type, table, whereClause, boundNames, selectsOnlyStaticColumns, type.allowUseOfSecondaryIndices(), allowFiltering, forView); + } + + /* + * We want to override allowUseOfSecondaryIndices flag from the StatementType for MV statements + * to avoid initing the Keyspace and SecondaryIndexManager. + */ + public StatementRestrictions(StatementType type, + TableMetadata table, + WhereClause whereClause, + VariableSpecifications boundNames, + boolean selectsOnlyStaticColumns, + boolean allowUseOfSecondaryIndices, + boolean allowFiltering, + boolean forView) + { this(type, table, allowFiltering); IndexRegistry indexRegistry = null; @@ -153,8 +169,7 @@ 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()) { @@ -178,7 +193,7 @@ public final class StatementRestrictions boolean hasQueriableClusteringColumnIndex = false; boolean hasQueriableIndex = false; - if (type.allowUseOfSecondaryIndices()) + if (allowUseOfSecondaryIndices) { if (whereClause.containsCustomExpressions()) processCustomIndexExpressions(whereClause.expressions, boundNames, indexRegistry); @@ -566,19 +581,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 = indexRegistry.getIndex(table.indexes.get(expression.targetIndex.getIdx()).get()); - + Index index = indexRegistry.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/207c80c1/src/java/org/apache/cassandra/cql3/selection/Selectable.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/selection/Selectable.java b/src/java/org/apache/cassandra/cql3/selection/Selectable.java index 998baca..220bb89 100644 --- a/src/java/org/apache/cassandra/cql3/selection/Selectable.java +++ b/src/java/org/apache/cassandra/cql3/selection/Selectable.java @@ -1296,6 +1296,12 @@ public interface Selectable extends AssignmentTestable { return new WithElementSelection(selected.prepare(cfm), element); } + + @Override + public String toString() + { + return String.format("%s[%s]", selected, element); + } } } @@ -1379,6 +1385,12 @@ public interface Selectable extends AssignmentTestable { return new WithSliceSelection(selected.prepare(cfm), from, to); } + + @Override + public String toString() + { + return String.format("%s[%s..%s]", selected, from == null ? "" : from, to == null ? "" : to); + } } } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/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 00d2b94..0000000 --- a/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.cassandra.cql3.statements; - -import org.apache.cassandra.audit.AuditLogEntryType; -import org.apache.cassandra.auth.Permission; -import org.apache.cassandra.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; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -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.isLocalSystemKeyspace(ksm.name)) - throw new InvalidRequestException("Cannot alter system keyspace"); - if (ksm.isVirtual()) - throw new InvalidRequestException("Cannot alter virtual keyspaces"); - - 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()); - } - - @Override - public String toString() - { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); - } - - @Override - public AuditLogContext getAuditLogContext() - { - return new AuditLogContext(AuditLogEntryType.ALTER_KEYSPACE, keyspace(), null); - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/207c80c1/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 957ac97..7a748e8 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AlterRoleStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AlterRoleStatement.java @@ -17,6 +17,7 @@ */ package org.apache.cassandra.cql3.statements; +import org.apache.cassandra.audit.AuditLogContext; import org.apache.cassandra.audit.AuditLogEntryType; import org.apache.cassandra.auth.*; import org.apache.cassandra.auth.IRoleManager.Option; @@ -58,13 +59,13 @@ public class AlterRoleStatement extends AuthenticationStatement if (opts.isEmpty() && dcPermissions == null) 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(); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
