User/role permissions for UDFs Patch by Sam Tunnicliffe; reviewed by Tyler Hobbs for CASSANDRA-7557
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/cb5897f3 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/cb5897f3 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/cb5897f3 Branch: refs/heads/trunk Commit: cb5897f3cb425334e693773fc88988de944fe247 Parents: d6a4b45 Author: Sam Tunnicliffe <[email protected]> Authored: Tue Apr 14 12:45:52 2015 -0500 Committer: Tyler Hobbs <[email protected]> Committed: Tue Apr 14 12:46:40 2015 -0500 ---------------------------------------------------------------------- CHANGES.txt | 1 + pylib/cqlshlib/cql3handling.py | 8 +- .../cassandra/auth/AuthMigrationListener.java | 10 + .../cassandra/auth/CassandraRoleManager.java | 8 +- .../apache/cassandra/auth/FunctionResource.java | 327 +++++++ .../org/apache/cassandra/auth/Permission.java | 7 +- .../org/apache/cassandra/auth/Resources.java | 2 + .../apache/cassandra/cql3/AbstractMarker.java | 8 + .../org/apache/cassandra/cql3/Attributes.java | 16 + .../org/apache/cassandra/cql3/CQLStatement.java | 7 +- .../apache/cassandra/cql3/ColumnCondition.java | 23 +- src/java/org/apache/cassandra/cql3/Cql.g | 27 +- src/java/org/apache/cassandra/cql3/Json.java | 15 +- src/java/org/apache/cassandra/cql3/Lists.java | 12 +- src/java/org/apache/cassandra/cql3/Maps.java | 11 +- .../org/apache/cassandra/cql3/Operation.java | 7 + src/java/org/apache/cassandra/cql3/Sets.java | 7 +- src/java/org/apache/cassandra/cql3/Term.java | 13 + src/java/org/apache/cassandra/cql3/Terms.java | 45 + src/java/org/apache/cassandra/cql3/Tuples.java | 10 +- .../org/apache/cassandra/cql3/UserTypes.java | 8 +- .../cql3/functions/AbstractFunction.java | 6 + .../cassandra/cql3/functions/Function.java | 3 +- .../cassandra/cql3/functions/FunctionCall.java | 15 +- .../cassandra/cql3/functions/UDAggregate.java | 8 +- .../ForwardingPrimaryKeyRestrictions.java | 7 + .../restrictions/MultiColumnRestriction.java | 33 +- .../restrictions/PrimaryKeyRestrictionSet.java | 7 + .../cql3/restrictions/Restriction.java | 3 + .../cql3/restrictions/RestrictionSet.java | 20 +- .../cql3/restrictions/Restrictions.java | 5 +- .../restrictions/SingleColumnRestriction.java | 46 +- .../restrictions/StatementRestrictions.java | 19 +- .../cassandra/cql3/restrictions/TermSlice.java | 17 + .../cql3/restrictions/TokenRestriction.java | 13 + .../selection/AbstractFunctionSelector.java | 6 + .../cassandra/cql3/selection/Selection.java | 27 +- .../cassandra/cql3/selection/Selector.java | 9 +- .../cql3/selection/SelectorFactories.java | 20 +- .../cql3/statements/BatchStatement.java | 16 +- .../statements/CreateAggregateStatement.java | 130 ++- .../statements/CreateFunctionStatement.java | 59 +- .../statements/CreateKeyspaceStatement.java | 14 +- .../cql3/statements/DropFunctionStatement.java | 121 ++- .../cql3/statements/ModificationStatement.java | 68 +- .../cql3/statements/ParsedStatement.java | 6 + .../PermissionsManagementStatement.java | 13 +- .../cql3/statements/SelectStatement.java | 21 +- .../apache/cassandra/service/ClientState.java | 31 + .../org/apache/cassandra/cql3/UFAuthTest.java | 945 +++++++++++++++++++ .../cassandra/cql3/UFIdentificationTest.java | 375 ++++++++ test/unit/org/apache/cassandra/cql3/UFTest.java | 45 +- 52 files changed, 2443 insertions(+), 237 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 4a1cd1c..973b1bd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.0 + * Add user/role permissions for user-defined functions (CASSANDRA-7557) * Allow cassandra config to be updated to restart daemon without unloading classes (CASSANDRA-9046) * Don't initialize compaction writer before checking if iter is empty (CASSANDRA-9117) * Remove line number generation from default logback.xml http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/pylib/cqlshlib/cql3handling.py ---------------------------------------------------------------------- diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py index a728fed..bc638b6 100644 --- a/pylib/cqlshlib/cql3handling.py +++ b/pylib/cqlshlib/cql3handling.py @@ -1207,7 +1207,7 @@ syntax_rules += r''' ''' syntax_rules += r''' -<grantStatement> ::= "GRANT" <permissionExpr> "ON" <resource> "TO" <rolename> +<grantStatement> ::= "GRANT" <permissionExpr> "ON" <resource> "TO" <rolename> ; <revokeStatement> ::= "REVOKE" <permissionExpr> "ON" <resource> "FROM" <rolename> @@ -1224,6 +1224,7 @@ syntax_rules += r''' | "SELECT" | "MODIFY" | "DESCRIBE" + | "EXECUTE" ; <permissionExpr> ::= ( <permission> "PERMISSION"? ) @@ -1232,6 +1233,7 @@ syntax_rules += r''' <resource> ::= <dataResource> | <roleResource> + | <functionResource> ; <dataResource> ::= ( "ALL" "KEYSPACES" ) @@ -1242,6 +1244,10 @@ syntax_rules += r''' <roleResource> ::= ("ALL" "ROLES") | ("ROLE" <rolename>) ; + +<functionResource> ::= ( "ALL" "FUNCTIONS" ("IN KEYSPACE" <keyspaceName>)? ) + | ("FUNCTION" <userFunctionName>) + ; ''' @completer_for('username', 'name') http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/auth/AuthMigrationListener.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/auth/AuthMigrationListener.java b/src/java/org/apache/cassandra/auth/AuthMigrationListener.java index f990bec..fe34329 100644 --- a/src/java/org/apache/cassandra/auth/AuthMigrationListener.java +++ b/src/java/org/apache/cassandra/auth/AuthMigrationListener.java @@ -17,7 +17,10 @@ */ package org.apache.cassandra.auth; +import java.util.List; + import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.service.MigrationListener; /** @@ -28,10 +31,17 @@ public class AuthMigrationListener extends MigrationListener public void onDropKeyspace(String ksName) { DatabaseDescriptor.getAuthorizer().revokeAllOn(DataResource.keyspace(ksName)); + DatabaseDescriptor.getAuthorizer().revokeAllOn(FunctionResource.keyspace(ksName)); } public void onDropColumnFamily(String ksName, String cfName) { DatabaseDescriptor.getAuthorizer().revokeAllOn(DataResource.table(ksName, cfName)); } + + public void onDropFunction(String ksName, String functionName, List<AbstractType<?>> argTypes) + { + DatabaseDescriptor.getAuthorizer() + .revokeAllOn(FunctionResource.function(ksName, functionName, argTypes)); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/auth/CassandraRoleManager.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/auth/CassandraRoleManager.java b/src/java/org/apache/cassandra/auth/CassandraRoleManager.java index 66b283d..261631d 100644 --- a/src/java/org/apache/cassandra/auth/CassandraRoleManager.java +++ b/src/java/org/apache/cassandra/auth/CassandraRoleManager.java @@ -187,15 +187,15 @@ public class CassandraRoleManager implements IRoleManager AuthKeyspace.NAME, AuthKeyspace.ROLES, escape(role.getRoleName()), - options.getSuperuser().get(), - options.getLogin().get(), + options.getSuperuser().or(false), + options.getLogin().or(false), escape(hashpw(options.getPassword().get()))) : String.format("INSERT INTO %s.%s (role, is_superuser, can_login) VALUES ('%s', %s, %s)", AuthKeyspace.NAME, AuthKeyspace.ROLES, escape(role.getRoleName()), - options.getSuperuser().get(), - options.getLogin().get()); + options.getSuperuser().or(false), + options.getLogin().or(false)); process(insertCql, consistencyForRole(role.getRoleName())); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/auth/FunctionResource.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/auth/FunctionResource.java b/src/java/org/apache/cassandra/auth/FunctionResource.java new file mode 100644 index 0000000..1421541 --- /dev/null +++ b/src/java/org/apache/cassandra/auth/FunctionResource.java @@ -0,0 +1,327 @@ +/* + * 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.auth; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import com.google.common.base.Joiner; +import com.google.common.base.Objects; +import com.google.common.base.Splitter; +import com.google.common.collect.Sets; +import org.apache.commons.lang3.StringUtils; + +import org.apache.cassandra.config.Schema; +import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.functions.Function; +import org.apache.cassandra.cql3.functions.FunctionName; +import org.apache.cassandra.cql3.functions.Functions; +import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.db.marshal.TypeParser; + +/** + * IResource implementation representing functions. + * + * The root level "functions" resource represents the collection of all Functions. + * "functions" - root level resource representing all functions defined across every keyspace + * "functions/keyspace" - keyspace level resource to apply permissions to all functions within a keyspace + * "functions/keyspace/function" - a specific function, scoped to a given keyspace + */ +public class FunctionResource implements IResource +{ + enum Level + { + ROOT, KEYSPACE, FUNCTION + } + + // permissions which may be granted on either a resource representing some collection of functions + // i.e. the root resource (all functions) or a keyspace level resource (all functions in a given keyspace) + private static final Set<Permission> COLLECTION_LEVEL_PERMISSIONS = Sets.immutableEnumSet(Permission.CREATE, + Permission.ALTER, + Permission.DROP, + Permission.AUTHORIZE, + Permission.EXECUTE); + // permissions which may be granted on resources representing a specific function + private static final Set<Permission> SCALAR_FUNCTION_PERMISSIONS = Sets.immutableEnumSet(Permission.ALTER, + Permission.DROP, + Permission.AUTHORIZE, + Permission.EXECUTE); + + private static final Set<Permission> AGGREGATE_FUNCTION_PERMISSIONS = Sets.immutableEnumSet(Permission.ALTER, + Permission.DROP, + Permission.AUTHORIZE, + Permission.EXECUTE); + + private static final String ROOT_NAME = "functions"; + private static final FunctionResource ROOT_RESOURCE = new FunctionResource(); + + private final Level level; + private final String keyspace; + private final String name; + private final List<AbstractType<?>> argTypes; + + private FunctionResource() + { + level = Level.ROOT; + keyspace = null; + name = null; + argTypes = null; + } + + private FunctionResource(String keyspace) + { + level = Level.KEYSPACE; + this.keyspace = keyspace; + name = null; + argTypes = null; + } + + private FunctionResource(String keyspace, String name, List<AbstractType<?>> argTypes) + { + level = Level.FUNCTION; + this.keyspace = keyspace; + this.name = name; + this.argTypes = argTypes; + } + + /** + * @return the root-level resource. + */ + public static FunctionResource root() + { + return ROOT_RESOURCE; + } + + /** + * Creates a FunctionResource representing the collection of functions scoped + * to a specific keyspace. + * + * @param keyspace name of the keyspace + * @return FunctionResource instance representing all of the keyspace's functions + */ + public static FunctionResource keyspace(String keyspace) + { + return new FunctionResource(keyspace); + } + + /** + * Creates a FunctionResource representing a specific, keyspace-scoped function. + * + * @param keyspace the keyspace in which the function is scoped + * @param name name of the function. + * @param argTypes the types of the arguments to the function + * @return FunctionResource instance reresenting the function. + */ + public static FunctionResource function(String keyspace, String name, List<AbstractType<?>> argTypes) + { + return new FunctionResource(keyspace, name, argTypes); + } + + /** + * Creates a FunctionResource representing a specific, keyspace-scoped function. + * This variant is used to create an instance during parsing of a CQL statement. + * It includes transposition of the arg types from CQL types to AbstractType + * implementations + * + * @param keyspace the keyspace in which the function is scoped + * @param name name of the function. + * @param argTypes the types of the function arguments in raw CQL form + * @return FunctionResource instance reresenting the function. + */ + public static FunctionResource functionFromCql(String keyspace, String name, List<CQL3Type.Raw> argTypes) + { + List<AbstractType<?>> abstractTypes = new ArrayList<>(); + for (CQL3Type.Raw cqlType : argTypes) + abstractTypes.add(cqlType.prepare(keyspace).getType()); + + return new FunctionResource(keyspace, name, abstractTypes); + } + + /** + * Parses a resource name into a FunctionResource instance. + * + * @param name Name of the function resource. + * @return FunctionResource instance matching the name. + */ + public static FunctionResource fromName(String name) + { + String[] parts = StringUtils.split(name, '/'); + + if (!parts[0].equals(ROOT_NAME) || parts.length > 3) + throw new IllegalArgumentException(String.format("%s is not a valid function resource name", name)); + + if (parts.length == 1) + return root(); + + if (parts.length == 2) + return keyspace(parts[1]); + + String[] nameAndArgs = StringUtils.split(parts[2], "[|]"); + return function(parts[1], nameAndArgs[0], argsListFromString(nameAndArgs[1])); + } + + /** + * @return Printable name of the resource. + */ + public String getName() + { + switch (level) + { + case ROOT: + return ROOT_NAME; + case KEYSPACE: + return String.format("%s/%s", ROOT_NAME, keyspace); + case FUNCTION: + return String.format("%s/%s/%s[%s]", ROOT_NAME, keyspace, name, argListAsString()); + } + throw new AssertionError(); + } + + /** + * Get the name of the keyspace this resource relates to. In the case of the + * global root resource, return null + * + * @return the keyspace name of this resource, or null for the root resource + */ + public String getKeyspace() + { + return keyspace; + } + + /** + * @return a qualified FunctionName instance for a function-level resource. + * Throws IllegalStateException if called on the resource which doens't represent a single function. + */ + public FunctionName getFunctionName() + { + if (level != Level.FUNCTION) + throw new IllegalStateException(String.format("%s function resource has no function name", level)); + return new FunctionName(keyspace, name); + } + + /** + * @return Parent of the resource, if any. Throws IllegalStateException if it's the root-level resource. + */ + public IResource getParent() + { + switch (level) + { + case KEYSPACE: + return root(); + case FUNCTION: + return keyspace(keyspace); + } + throw new IllegalStateException("Root-level resource can't have a parent"); + } + + public boolean hasParent() + { + return level != Level.ROOT; + } + + public boolean exists() + { + switch (level) + { + case ROOT: + return true; + case KEYSPACE: + return Schema.instance.getKeyspaces().contains(keyspace); + case FUNCTION: + return Functions.find(getFunctionName(), argTypes) != null; + } + throw new AssertionError(); + } + + public Set<Permission> applicablePermissions() + { + switch (level) + { + case ROOT: + case KEYSPACE: + return COLLECTION_LEVEL_PERMISSIONS; + case FUNCTION: + { + Function function = Functions.find(getFunctionName(), argTypes); + assert function != null : "Unable to find function object for resource " + toString(); + return function.isAggregate() ? AGGREGATE_FUNCTION_PERMISSIONS : SCALAR_FUNCTION_PERMISSIONS; + } + } + throw new AssertionError(); + } + + public int compareTo(FunctionResource o) + { + return this.name.compareTo(o.name); + } + + @Override + public String toString() + { + switch (level) + { + case ROOT: + return "<all functions>"; + case KEYSPACE: + return String.format("<all functions in %s>", keyspace); + case FUNCTION: + return String.format("<function %s.%s(%s)>", + keyspace, + name, + Joiner.on(", ").join(AbstractType.asCQLTypeStringList(argTypes))); + } + throw new AssertionError(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + return true; + + if (!(o instanceof FunctionResource)) + return false; + + FunctionResource f = (FunctionResource) o; + + return Objects.equal(level, f.level) + && Objects.equal(keyspace, f.keyspace) + && Objects.equal(name, f.name) + && Objects.equal(argTypes, f.argTypes); + } + + @Override + public int hashCode() + { + return Objects.hashCode(level, keyspace, name, argTypes); + } + + private String argListAsString() + { + return Joiner.on("^").join(argTypes); + } + + private static List<AbstractType<?>> argsListFromString(String s) + { + List<AbstractType<?>> argTypes = new ArrayList<>(); + for(String type : Splitter.on("^").omitEmptyStrings().trimResults().split(s)) + argTypes.add(TypeParser.parse(type)); + return argTypes; + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/auth/Permission.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/auth/Permission.java b/src/java/org/apache/cassandra/auth/Permission.java index 320d745..d552280 100644 --- a/src/java/org/apache/cassandra/auth/Permission.java +++ b/src/java/org/apache/cassandra/auth/Permission.java @@ -58,9 +58,12 @@ public enum Permission // permission management AUTHORIZE, // required for GRANT and REVOKE of permissions or roles. - DESCRIBE; // required on the root-level RoleResource to list all Roles + DESCRIBE, // required on the root-level RoleResource to list all Roles + + // UDF permissions + EXECUTE; // required to invoke any user defined function or aggregate public static final Set<Permission> ALL = - Sets.immutableEnumSet(EnumSet.range(Permission.CREATE, Permission.DESCRIBE)); + Sets.immutableEnumSet(EnumSet.range(Permission.CREATE, Permission.EXECUTE)); public static final Set<Permission> NONE = ImmutableSet.of(); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/auth/Resources.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/auth/Resources.java b/src/java/org/apache/cassandra/auth/Resources.java index 393e18a..ebcfc16 100644 --- a/src/java/org/apache/cassandra/auth/Resources.java +++ b/src/java/org/apache/cassandra/auth/Resources.java @@ -56,6 +56,8 @@ public final class Resources return RoleResource.fromName(name); else if (name.startsWith(DataResource.root().getName())) return DataResource.fromName(name); + else if (name.startsWith(FunctionResource.root().getName())) + return FunctionResource.fromName(name); else throw new IllegalArgumentException(String.format("Name %s is not valid for any resource type", name)); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/AbstractMarker.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/AbstractMarker.java b/src/java/org/apache/cassandra/cql3/AbstractMarker.java index 87344b6..d11b8e2 100644 --- a/src/java/org/apache/cassandra/cql3/AbstractMarker.java +++ b/src/java/org/apache/cassandra/cql3/AbstractMarker.java @@ -17,6 +17,9 @@ */ package org.apache.cassandra.cql3; +import java.util.Collections; + +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.marshal.CollectionType; import org.apache.cassandra.db.marshal.ListType; import org.apache.cassandra.exceptions.InvalidRequestException; @@ -45,6 +48,11 @@ public abstract class AbstractMarker extends Term.NonTerminal return true; } + public Iterable<Function> getFunctions() + { + return Collections.emptySet(); + } + /** * A parsed, but non prepared, bind marker. */ http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/Attributes.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Attributes.java b/src/java/org/apache/cassandra/cql3/Attributes.java index 0ab42a2..4136ec5 100644 --- a/src/java/org/apache/cassandra/cql3/Attributes.java +++ b/src/java/org/apache/cassandra/cql3/Attributes.java @@ -18,7 +18,11 @@ package org.apache.cassandra.cql3; import java.nio.ByteBuffer; +import java.util.Collections; +import com.google.common.collect.Iterables; + +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.ExpiringCell; import org.apache.cassandra.db.marshal.Int32Type; import org.apache.cassandra.db.marshal.LongType; @@ -51,6 +55,18 @@ public class Attributes || (timeToLive != null && timeToLive.usesFunction(ksName, functionName)); } + public Iterable<Function> getFunctions() + { + if (timestamp != null && timeToLive != null) + return Iterables.concat(timestamp.getFunctions(), timeToLive.getFunctions()); + else if (timestamp != null) + return timestamp.getFunctions(); + else if (timeToLive != null) + return timeToLive.getFunctions(); + else + return Collections.emptySet(); + } + public boolean isTimestampSet() { return timestamp != null; http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/CQLStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/CQLStatement.java b/src/java/org/apache/cassandra/cql3/CQLStatement.java index d555ec3..80220a8 100644 --- a/src/java/org/apache/cassandra/cql3/CQLStatement.java +++ b/src/java/org/apache/cassandra/cql3/CQLStatement.java @@ -17,10 +17,11 @@ */ package org.apache.cassandra.cql3; -import org.apache.cassandra.transport.messages.ResultMessage; +import org.apache.cassandra.cql3.functions.Function; +import org.apache.cassandra.exceptions.*; import org.apache.cassandra.service.ClientState; import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.exceptions.*; +import org.apache.cassandra.transport.messages.ResultMessage; public interface CQLStatement { @@ -60,4 +61,6 @@ public interface CQLStatement public ResultMessage executeInternal(QueryState state, QueryOptions options) throws RequestValidationException, RequestExecutionException; boolean usesFunction(String ksName, String functionName); + + public Iterable<Function> getFunctions(); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/ColumnCondition.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/ColumnCondition.java b/src/java/org/apache/cassandra/cql3/ColumnCondition.java index dfe2415..dcd8bca 100644 --- a/src/java/org/apache/cassandra/cql3/ColumnCondition.java +++ b/src/java/org/apache/cassandra/cql3/ColumnCondition.java @@ -21,21 +21,24 @@ import java.nio.ByteBuffer; import java.util.*; import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; -import static com.google.common.collect.Lists.newArrayList; import org.apache.cassandra.config.ColumnDefinition; -import org.apache.cassandra.db.*; +import org.apache.cassandra.cql3.functions.Function; +import org.apache.cassandra.db.Cell; +import org.apache.cassandra.db.ColumnFamily; import org.apache.cassandra.db.composites.CellName; import org.apache.cassandra.db.composites.CellNameType; import org.apache.cassandra.db.composites.Composite; import org.apache.cassandra.db.filter.ColumnSlice; import org.apache.cassandra.db.marshal.*; import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.serializers.CollectionSerializer; import org.apache.cassandra.transport.Server; import org.apache.cassandra.utils.ByteBufferUtil; +import static com.google.common.collect.Lists.newArrayList; + /** * A CQL3 condition on the value of a column or collection element. For example, "UPDATE .. IF a = 0". */ @@ -107,6 +110,20 @@ public class ColumnCondition return false; } + public Iterable<Function> getFunctions() + { + Iterable<Function> iter = Collections.emptyList(); + if (collectionElement != null) + iter = Iterables.concat(iter, collectionElement.getFunctions()); + if (value != null) + iter = Iterables.concat(iter, value.getFunctions()); + if (inValues != null) + for (Term value : inValues) + if (value != null) + iter = Iterables.concat(iter, value.getFunctions()); + return iter; + } + /** * Collects the column specification for the bind variables of this operation. * http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/Cql.g ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g index e5b0efd..ca68396 100644 --- a/src/java/org/apache/cassandra/cql3/Cql.g +++ b/src/java/org/apache/cassandra/cql3/Cql.g @@ -913,7 +913,7 @@ listPermissionsStatement returns [ListPermissionsStatement stmt] ; permission returns [Permission perm] - : p=(K_CREATE | K_ALTER | K_DROP | K_SELECT | K_MODIFY | K_AUTHORIZE | K_DESCRIBE) + : p=(K_CREATE | K_ALTER | K_DROP | K_SELECT | K_MODIFY | K_AUTHORIZE | K_DESCRIBE | K_EXECUTE) { $perm = Permission.valueOf($p.text.toUpperCase()); } ; @@ -925,6 +925,7 @@ permissionOrAll returns [Set<Permission> perms] resource returns [IResource res] : d=dataResource { $res = $d.res; } | r=roleResource { $res = $r.res; } + | f=functionResource { $res = $f.res; } ; dataResource returns [DataResource res] @@ -934,11 +935,30 @@ dataResource returns [DataResource res] { $res = DataResource.table($cf.name.getKeyspace(), $cf.name.getColumnFamily()); } ; -roleResource returns [RoleResource res] +roleResource returns [RoleResource res] : K_ALL K_ROLES { $res = RoleResource.root(); } | K_ROLE role = userOrRoleName { $res = RoleResource.role($role.name.getName()); } ; +functionResource returns [FunctionResource res] + @init { + List<CQL3Type.Raw> argsTypes = new ArrayList<>(); + } + : K_ALL K_FUNCTIONS { $res = FunctionResource.root(); } + | K_ALL K_FUNCTIONS K_IN K_KEYSPACE ks = keyspaceName { $res = FunctionResource.keyspace($ks.id); } + // Arg types are mandatory for DCL statements on Functions + | K_FUNCTION fn=functionName + ( + '(' + ( + v=comparatorType { argsTypes.add(v); } + ( ',' v=comparatorType { argsTypes.add(v); } )* + )? + ')' + ) + { $res = FunctionResource.functionFromCql($fn.s.keyspace, $fn.s.name, argsTypes); } + ; + /** * CREATE USER [IF NOT EXISTS] <username> [WITH PASSWORD <password>] [SUPERUSER|NOSUPERUSER] */ @@ -1522,6 +1542,7 @@ basic_unreserved_keyword returns [String str] | K_FROZEN | K_TUPLE | K_FUNCTION + | K_FUNCTIONS | K_AGGREGATE | K_SFUNC | K_STYPE @@ -1601,6 +1622,7 @@ K_REVOKE: R E V O K E; K_MODIFY: M O D I F Y; K_AUTHORIZE: A U T H O R I Z E; K_DESCRIBE: D E S C R I B E; +K_EXECUTE: E X E C U T E; K_NORECURSIVE: N O R E C U R S I V E; K_USER: U S E R; @@ -1650,6 +1672,7 @@ K_STATIC: S T A T I C; K_FROZEN: F R O Z E N; K_FUNCTION: F U N C T I O N; +K_FUNCTIONS: F U N C T I O N S; K_AGGREGATE: A G G R E G A T E; K_SFUNC: S F U N C; K_STYPE: S T Y P E; http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/Json.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Json.java b/src/java/org/apache/cassandra/cql3/Json.java index 8929cc0..905f6e0 100644 --- a/src/java/org/apache/cassandra/cql3/Json.java +++ b/src/java/org/apache/cassandra/cql3/Json.java @@ -17,18 +17,19 @@ */ package org.apache.cassandra.cql3; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.*; + import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.serializers.MarshalException; import org.codehaus.jackson.io.JsonStringEncoder; import org.codehaus.jackson.map.ObjectMapper; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.*; - /** Term-related classes for INSERT JSON support. */ public class Json { @@ -251,6 +252,12 @@ public class Json Term term = marker.getValue(column); return term == null ? null : term.bind(options); } + + @Override + public Iterable<Function> getFunctions() + { + return Collections.emptyList(); + } } /** http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/Lists.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Lists.java b/src/java/org/apache/cassandra/cql3/Lists.java index ae2417e..8b8375d 100644 --- a/src/java/org/apache/cassandra/cql3/Lists.java +++ b/src/java/org/apache/cassandra/cql3/Lists.java @@ -23,31 +23,26 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.cassandra.config.ColumnDefinition; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.Cell; import org.apache.cassandra.db.ColumnFamily; import org.apache.cassandra.db.composites.CellName; import org.apache.cassandra.db.composites.Composite; -import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.Int32Type; import org.apache.cassandra.db.marshal.ListType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.serializers.CollectionSerializer; -import org.apache.cassandra.serializers.ListSerializer; import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.transport.Server; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.UUIDGen; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Static helper methods and classes for lists. */ public abstract class Lists { - private static final Logger logger = LoggerFactory.getLogger(Lists.class); - private Lists() {} public static ColumnSpecification indexSpecOf(ColumnSpecification column) @@ -224,6 +219,11 @@ public abstract class Lists } return new Value(buffers); } + + public Iterable<Function> getFunctions() + { + return Terms.getFunctions(elements); + } } /** http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/Maps.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Maps.java b/src/java/org/apache/cassandra/cql3/Maps.java index d91b9d8..6c7cfa6 100644 --- a/src/java/org/apache/cassandra/cql3/Maps.java +++ b/src/java/org/apache/cassandra/cql3/Maps.java @@ -20,15 +20,16 @@ package org.apache.cassandra.cql3; import java.nio.ByteBuffer; import java.util.*; +import com.google.common.collect.Iterables; + import org.apache.cassandra.config.ColumnDefinition; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.ColumnFamily; import org.apache.cassandra.db.composites.CellName; import org.apache.cassandra.db.composites.Composite; -import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.MapType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.serializers.CollectionSerializer; -import org.apache.cassandra.serializers.MapSerializer; import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.transport.Server; import org.apache.cassandra.utils.FBUtilities; @@ -247,6 +248,12 @@ public abstract class Maps } return new Value(buffers); } + + public Iterable<Function> getFunctions() + { + return Iterables.concat(Terms.getFunctions(elements.keySet()), + Terms.getFunctions(elements.values())); + } } public static class Marker extends AbstractMarker http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/Operation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Operation.java b/src/java/org/apache/cassandra/cql3/Operation.java index cc32647..d99a00d 100644 --- a/src/java/org/apache/cassandra/cql3/Operation.java +++ b/src/java/org/apache/cassandra/cql3/Operation.java @@ -18,8 +18,10 @@ package org.apache.cassandra.cql3; import java.nio.ByteBuffer; +import java.util.Collections; import org.apache.cassandra.config.ColumnDefinition; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.ColumnFamily; import org.apache.cassandra.db.composites.Composite; import org.apache.cassandra.db.marshal.*; @@ -60,6 +62,11 @@ public abstract class Operation return t != null && t.usesFunction(ksName, functionName); } + public Iterable<Function> getFunctions() + { + return t != null ? t.getFunctions() : Collections.<Function>emptySet(); + } + /** * @return whether the operation requires a read of the previous value to be executed * (only lists setterByIdx, discard and discardByIdx requires that). http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/Sets.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Sets.java b/src/java/org/apache/cassandra/cql3/Sets.java index e0a53ad..ce372f8 100644 --- a/src/java/org/apache/cassandra/cql3/Sets.java +++ b/src/java/org/apache/cassandra/cql3/Sets.java @@ -23,6 +23,7 @@ import java.util.*; import com.google.common.base.Joiner; import org.apache.cassandra.config.ColumnDefinition; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.ColumnFamily; import org.apache.cassandra.db.composites.CellName; import org.apache.cassandra.db.composites.Composite; @@ -32,7 +33,6 @@ import org.apache.cassandra.db.marshal.SetType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.serializers.CollectionSerializer; import org.apache.cassandra.serializers.MarshalException; -import org.apache.cassandra.serializers.SetSerializer; import org.apache.cassandra.transport.Server; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.FBUtilities; @@ -222,6 +222,11 @@ public abstract class Sets } return new Value(buffers); } + + public Iterable<Function> getFunctions() + { + return Terms.getFunctions(elements); + } } public static class Marker extends AbstractMarker http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/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 fe8e51e..6997f78 100644 --- a/src/java/org/apache/cassandra/cql3/Term.java +++ b/src/java/org/apache/cassandra/cql3/Term.java @@ -18,8 +18,11 @@ package org.apache.cassandra.cql3; import java.nio.ByteBuffer; +import java.util.Collections; import java.util.List; +import java.util.Set; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.exceptions.InvalidRequestException; /** @@ -69,6 +72,8 @@ public interface Term boolean usesFunction(String ksName, String functionName); + Iterable<Function> getFunctions(); + /** * A parsed, non prepared (thus untyped) term. * @@ -122,6 +127,11 @@ public interface Term return false; } + public Set<Function> getFunctions() + { + return Collections.emptySet(); + } + // While some NonTerminal may not have bind markers, no Term can be Terminal // with a bind marker public boolean containsBindMarker() @@ -158,6 +168,9 @@ public interface Term */ public abstract class NonTerminal implements Term { + // TODO - this is not necessarily false, yet isn't overridden in concrete classes + // representing collection literals + // e,g "UPDATE table SET map_col = { key_function() : val_function() }) WHERE .... public boolean usesFunction(String ksName, String functionName) { return false; http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/Terms.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Terms.java b/src/java/org/apache/cassandra/cql3/Terms.java new file mode 100644 index 0000000..0b049b9 --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/Terms.java @@ -0,0 +1,45 @@ +/* + * 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.Collections; + +import com.google.common.collect.Iterables; + +import org.apache.cassandra.cql3.functions.Function; + +public class Terms +{ + + private static com.google.common.base.Function<Term, Iterable<Function>> TO_FUNCTION_ITERABLE = + new com.google.common.base.Function<Term, Iterable<Function>>() + { + public Iterable<Function> apply(Term term) + { + return term.getFunctions(); + } + }; + + public static Iterable<Function> getFunctions(Iterable<Term> terms) + { + if (terms == null) + return Collections.emptySet(); + + return Iterables.concat(Iterables.transform(terms, TO_FUNCTION_ITERABLE)); + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/Tuples.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Tuples.java b/src/java/org/apache/cassandra/cql3/Tuples.java index 6d82218..967ce37 100644 --- a/src/java/org/apache/cassandra/cql3/Tuples.java +++ b/src/java/org/apache/cassandra/cql3/Tuples.java @@ -18,12 +18,15 @@ package org.apache.cassandra.cql3; import java.nio.ByteBuffer; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.cql3.Term.MultiColumnRaw; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.marshal.*; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.serializers.MarshalException; @@ -226,6 +229,11 @@ public class Tuples { return tupleToString(elements); } + + public Iterable<Function> getFunctions() + { + return Terms.getFunctions(elements); + } } /** http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/UserTypes.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/UserTypes.java b/src/java/org/apache/cassandra/cql3/UserTypes.java index 934344c..f00a376 100644 --- a/src/java/org/apache/cassandra/cql3/UserTypes.java +++ b/src/java/org/apache/cassandra/cql3/UserTypes.java @@ -20,9 +20,10 @@ package org.apache.cassandra.cql3; import java.nio.ByteBuffer; import java.util.*; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.marshal.CollectionType; -import org.apache.cassandra.db.marshal.UserType; import org.apache.cassandra.db.marshal.UTF8Type; +import org.apache.cassandra.db.marshal.UserType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.transport.Server; @@ -157,6 +158,11 @@ public abstract class UserTypes return false; } + public Iterable<Function> getFunctions() + { + return Terms.getFunctions(values); + } + public boolean containsBindMarker() { for (Term t : values) http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/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 e2d69b8..15ae757 100644 --- a/src/java/org/apache/cassandra/cql3/functions/AbstractFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/AbstractFunction.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.functions; import java.util.List; import com.google.common.base.Objects; +import com.google.common.collect.ImmutableSet; import org.apache.cassandra.db.marshal.AbstractType; @@ -71,6 +72,11 @@ public abstract class AbstractFunction implements Function return name.keyspace.equals(ksName) && name.name.equals(functionName); } + public Iterable<Function> getFunctions() + { + return ImmutableSet.<Function>of(this); + } + public boolean hasReferenceTo(Function function) { return false; http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/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 1b7952a..3c2e5a7 100644 --- a/src/java/org/apache/cassandra/cql3/functions/Function.java +++ b/src/java/org/apache/cassandra/cql3/functions/Function.java @@ -20,7 +20,6 @@ package org.apache.cassandra.cql3.functions; import java.util.List; import org.apache.cassandra.db.marshal.AbstractType; - import org.github.jamm.Unmetered; @Unmetered @@ -53,5 +52,7 @@ public interface Function boolean usesFunction(String ksName, String functionName); + Iterable<Function> getFunctions(); + boolean hasReferenceTo(Function function); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java index a3bd669..d8efa7f 100644 --- a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java +++ b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java @@ -21,15 +21,13 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import com.google.common.collect.Iterables; + import org.apache.cassandra.cql3.*; -import org.apache.cassandra.db.marshal.CollectionType; -import org.apache.cassandra.db.marshal.ListType; -import org.apache.cassandra.db.marshal.MapType; -import org.apache.cassandra.db.marshal.SetType; +import org.apache.cassandra.db.marshal.*; import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.transport.Server; -import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.serializers.MarshalException; +import org.apache.cassandra.utils.ByteBufferUtil; public class FunctionCall extends Term.NonTerminal { @@ -47,6 +45,11 @@ public class FunctionCall extends Term.NonTerminal return fun.usesFunction(ksName, functionName); } + public Iterable<Function> getFunctions() + { + return Iterables.concat(Terms.getFunctions(terms), fun.getFunctions()); + } + public void collectMarkerSpecification(VariableSpecifications boundNames) { for (Term t : terms) http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/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 e9c33ba..59f8daa 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java @@ -21,11 +21,12 @@ import java.nio.ByteBuffer; import java.util.*; import com.google.common.base.Objects; +import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.db.marshal.AbstractType; -import org.apache.cassandra.exceptions.*; +import org.apache.cassandra.exceptions.InvalidRequestException; /** * Base class for user-defined-aggregates. @@ -104,6 +105,11 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction || finalFunction != null && finalFunction.name().keyspace.equals(ksName) && finalFunction.name().name.equals(functionName); } + public Iterable<Function> getFunctions() + { + return ImmutableSet.of(this, stateFunction, finalFunction); + } + public boolean isAggregate() { return true; http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java index 537481f..fd40b7a 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java @@ -23,6 +23,7 @@ import java.util.List; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.statements.Bound; import org.apache.cassandra.db.IndexExpression; import org.apache.cassandra.db.composites.Composite; @@ -50,6 +51,12 @@ abstract class ForwardingPrimaryKeyRestrictions implements PrimaryKeyRestriction } @Override + public Iterable<Function> getFunctions() + { + return getDelegate().getFunctions(); + } + + @Override public Collection<ColumnDefinition> getColumnDefs() { return getDelegate().getColumnDefs(); http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java index be6d905..3a755f0 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java @@ -21,14 +21,11 @@ import java.nio.ByteBuffer; import java.util.*; import org.apache.cassandra.config.ColumnDefinition; -import org.apache.cassandra.cql3.AbstractMarker; -import org.apache.cassandra.cql3.Operator; -import org.apache.cassandra.cql3.QueryOptions; -import org.apache.cassandra.cql3.Term; -import org.apache.cassandra.cql3.Tuples; +import org.apache.cassandra.cql3.*; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.statements.Bound; import org.apache.cassandra.db.IndexExpression; -import org.apache.cassandra.db.composites.*; +import org.apache.cassandra.db.composites.CompositesBuilder; import org.apache.cassandra.db.index.SecondaryIndex; import org.apache.cassandra.db.index.SecondaryIndexManager; import org.apache.cassandra.exceptions.InvalidRequestException; @@ -144,6 +141,12 @@ public abstract class MultiColumnRestriction extends AbstractRestriction } @Override + public Iterable<Function> getFunctions() + { + return value.getFunctions(); + } + + @Override public String toString() { return String.format("EQ(%s)", value); @@ -273,6 +276,12 @@ public abstract class MultiColumnRestriction extends AbstractRestriction } @Override + public Iterable<Function> getFunctions() + { + return Terms.getFunctions(values); + } + + @Override public String toString() { return String.format("IN(%s)", values); @@ -312,6 +321,12 @@ public abstract class MultiColumnRestriction extends AbstractRestriction } @Override + public Iterable<Function> getFunctions() + { + return Collections.emptySet(); + } + + @Override public String toString() { return "IN ?"; @@ -387,6 +402,12 @@ public abstract class MultiColumnRestriction extends AbstractRestriction } @Override + public Iterable<Function> getFunctions() + { + return slice.getFunctions(); + } + + @Override public boolean isInclusive(Bound b) { return slice.isInclusive(b); http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java index 194f4d5..ab61628 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java @@ -24,6 +24,7 @@ import java.util.List; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.statements.Bound; import org.apache.cassandra.db.IndexExpression; import org.apache.cassandra.db.composites.*; @@ -146,6 +147,12 @@ final class PrimaryKeyRestrictionSet extends AbstractPrimaryKeyRestrictions } @Override + public Iterable<Function> getFunctions() + { + return restrictions.getFunctions(); + } + + @Override public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException { if (restriction.isOnToken()) http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java index 49af20c..71dc373 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java @@ -22,6 +22,7 @@ import java.util.List; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.statements.Bound; import org.apache.cassandra.db.IndexExpression; import org.apache.cassandra.db.composites.CompositesBuilder; @@ -70,6 +71,8 @@ public interface Restriction */ public boolean usesFunction(String ksName, String functionName); + public Iterable<Function> getFunctions(); + /** * Checks if the specified bound is set or not. * @param b the bound type http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java index b422749..3a236cc 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java @@ -19,8 +19,11 @@ package org.apache.cassandra.cql3.restrictions; import java.util.*; +import com.google.common.collect.Iterables; + import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.restrictions.SingleColumnRestriction.Contains; import org.apache.cassandra.db.IndexExpression; import org.apache.cassandra.db.index.SecondaryIndexManager; @@ -88,6 +91,21 @@ final class RestrictionSet implements Restrictions, Iterable<Restriction> } @Override + public Iterable<Function> getFunctions() + { + com.google.common.base.Function<Restriction, Iterable<Function>> transform = + new com.google.common.base.Function<Restriction, Iterable<Function>>() + { + public Iterable<Function> apply(Restriction restriction) + { + return restriction.getFunctions(); + } + }; + + return Iterables.concat(Iterables.transform(restrictions.values(), transform)); + } + + @Override public final boolean isEmpty() { return getColumnDefs().isEmpty(); @@ -249,4 +267,4 @@ final class RestrictionSet implements Restrictions, Iterable<Restriction> { return new LinkedHashSet<>(restrictions.values()).iterator(); } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java index e2b31dd..7487734 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java @@ -22,6 +22,7 @@ import java.util.List; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.IndexExpression; import org.apache.cassandra.db.index.SecondaryIndexManager; import org.apache.cassandra.exceptions.InvalidRequestException; @@ -46,6 +47,8 @@ interface Restrictions */ public boolean usesFunction(String ksName, String functionName); + public Iterable<Function> getFunctions(); + /** * Check if the restriction is on indexed columns. * @@ -81,4 +84,4 @@ interface Restrictions * @return the number of columns that have a restriction. */ public int size(); -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java index b25d5a4..ad4893f 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java @@ -18,16 +18,13 @@ package org.apache.cassandra.cql3.restrictions; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; + +import com.google.common.collect.Iterables; import org.apache.cassandra.config.ColumnDefinition; -import org.apache.cassandra.cql3.AbstractMarker; -import org.apache.cassandra.cql3.Operator; -import org.apache.cassandra.cql3.QueryOptions; -import org.apache.cassandra.cql3.Term; +import org.apache.cassandra.cql3.*; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.statements.Bound; import org.apache.cassandra.db.IndexExpression; import org.apache.cassandra.db.composites.CompositesBuilder; @@ -114,6 +111,12 @@ public abstract class SingleColumnRestriction extends AbstractRestriction } @Override + public Iterable getFunctions() + { + return value.getFunctions(); + } + + @Override public boolean isEQ() { return true; @@ -220,6 +223,12 @@ public abstract class SingleColumnRestriction extends AbstractRestriction } @Override + public Iterable<Function> getFunctions() + { + return Terms.getFunctions(values); + } + + @Override protected List<ByteBuffer> getValues(QueryOptions options) throws InvalidRequestException { List<ByteBuffer> buffers = new ArrayList<>(values.size()); @@ -252,6 +261,12 @@ public abstract class SingleColumnRestriction extends AbstractRestriction } @Override + public Iterable<Function> getFunctions() + { + return Collections.emptySet(); + } + + @Override protected List<ByteBuffer> getValues(QueryOptions options) throws InvalidRequestException { Term.MultiItemTerminal lval = (Term.MultiItemTerminal) marker.bind(options); @@ -285,6 +300,12 @@ public abstract class SingleColumnRestriction extends AbstractRestriction } @Override + public Iterable<Function> getFunctions() + { + return slice.getFunctions(); + } + + @Override public boolean isSlice() { return true; @@ -483,6 +504,15 @@ public abstract class SingleColumnRestriction extends AbstractRestriction } @Override + public Iterable<Function> getFunctions() + { + return Iterables.concat(Terms.getFunctions(values), + Terms.getFunctions(keys), + Terms.getFunctions(entryKeys), + Terms.getFunctions(entryValues)); + } + + @Override public String toString() { return String.format("CONTAINS(values=%s, keys=%s, entryKeys=%s, entryValues=%s)", values, keys, entryKeys, entryValues); http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/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 cea1699..dcaad47 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java @@ -21,18 +21,14 @@ import java.nio.ByteBuffer; import java.util.*; import com.google.common.base.Joiner; +import com.google.common.collect.Iterables; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; -import org.apache.cassandra.cql3.ColumnIdentifier; -import org.apache.cassandra.cql3.QueryOptions; -import org.apache.cassandra.cql3.Relation; -import org.apache.cassandra.cql3.VariableSpecifications; +import org.apache.cassandra.cql3.*; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.statements.Bound; -import org.apache.cassandra.db.ColumnFamilyStore; -import org.apache.cassandra.db.IndexExpression; -import org.apache.cassandra.db.Keyspace; -import org.apache.cassandra.db.RowPosition; +import org.apache.cassandra.db.*; import org.apache.cassandra.db.composites.Composite; import org.apache.cassandra.db.index.SecondaryIndexManager; import org.apache.cassandra.dht.*; @@ -184,6 +180,13 @@ public final class StatementRestrictions || nonPrimaryKeyRestrictions.usesFunction(ksName, functionName); } + public Iterable<Function> getFunctions() + { + return Iterables.concat(partitionKeyRestrictions.getFunctions(), + clusteringColumnsRestrictions.getFunctions(), + nonPrimaryKeyRestrictions.getFunctions()); + } + private void addSingleColumnRestriction(SingleColumnRestriction restriction) throws InvalidRequestException { ColumnDefinition def = restriction.columnDef; http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/restrictions/TermSlice.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/TermSlice.java b/src/java/org/apache/cassandra/cql3/restrictions/TermSlice.java index 3622220..d082cc3 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/TermSlice.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/TermSlice.java @@ -17,8 +17,13 @@ */ package org.apache.cassandra.cql3.restrictions; +import java.util.Collections; + +import com.google.common.collect.Iterables; + import org.apache.cassandra.cql3.Operator; import org.apache.cassandra.cql3.Term; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.statements.Bound; import org.apache.cassandra.db.index.SecondaryIndex; @@ -164,4 +169,16 @@ final class TermSlice return supported; } + + public Iterable<Function> getFunctions() + { + if (hasBound(Bound.START) && hasBound(Bound.END)) + return Iterables.concat(bound(Bound.START).getFunctions(), bound(Bound.END).getFunctions()); + else if (hasBound(Bound.START)) + return bound(Bound.START).getFunctions(); + else if (hasBound(Bound.END)) + return bound(Bound.END).getFunctions(); + else + return Collections.emptySet(); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java index 5848c91..5a00bd4 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java @@ -27,6 +27,7 @@ import com.google.common.base.Joiner; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.Term; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.statements.Bound; import org.apache.cassandra.db.IndexExpression; import org.apache.cassandra.db.composites.CType; @@ -178,6 +179,12 @@ public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions } @Override + public Iterable<Function> getFunctions() + { + return value.getFunctions(); + } + + @Override protected PrimaryKeyRestrictions doMergeWith(TokenRestriction otherRestriction) throws InvalidRequestException { throw invalidRequest("%s cannot be restricted by more than one relation if it includes an Equal", @@ -233,6 +240,12 @@ public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions } @Override + public Iterable<Function> getFunctions() + { + return slice.getFunctions(); + } + + @Override public boolean isInclusive(Bound b) { return slice.isInclusive(b); http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java b/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java index d6a0c71..11e7e48 100644 --- a/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java @@ -21,6 +21,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; +import com.google.common.collect.Iterables; import org.apache.commons.lang3.text.StrBuilder; import org.apache.cassandra.cql3.functions.Function; @@ -72,6 +73,11 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector return fun.usesFunction(ksName, functionName); } + public Iterable<Function> getFunctions() + { + return Iterables.concat(fun.getFunctions(), factories.getFunctions()); + } + public Selector newInstance() throws InvalidRequestException { return fun.isAggregate() ? new AggregateFunctionSelector(fun, factories.newInstances()) http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/selection/Selection.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/selection/Selection.java b/src/java/org/apache/cassandra/cql3/selection/Selection.java index cbca97d..25bce78 100644 --- a/src/java/org/apache/cassandra/cql3/selection/Selection.java +++ b/src/java/org/apache/cassandra/cql3/selection/Selection.java @@ -20,12 +20,15 @@ package org.apache.cassandra.cql3.selection; import java.nio.ByteBuffer; import java.util.*; +import com.google.common.base.Objects; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; + import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; -import org.apache.cassandra.cql3.ColumnIdentifier; -import org.apache.cassandra.cql3.ColumnSpecification; -import org.apache.cassandra.cql3.Json; -import org.apache.cassandra.cql3.ResultSet; +import org.apache.cassandra.cql3.*; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.Cell; import org.apache.cassandra.db.CounterCell; import org.apache.cassandra.db.ExpiringCell; @@ -34,11 +37,6 @@ import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.utils.ByteBufferUtil; -import com.google.common.base.Predicate; -import com.google.common.base.Objects; -import com.google.common.collect.Iterables; -import com.google.common.collect.Iterators; - public abstract class Selection { /** @@ -181,6 +179,11 @@ public abstract class Selection return false; } + public Iterable<Function> getFunctions() + { + return Collections.emptySet(); + } + private static boolean processesSelection(List<RawSelector> rawSelectors) { for (RawSelector rawSelector : rawSelectors) @@ -486,6 +489,12 @@ public abstract class Selection } @Override + public Iterable<Function> getFunctions() + { + return factories.getFunctions(); + } + + @Override public int addColumnForOrdering(ColumnDefinition c) { int index = super.addColumnForOrdering(c); http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/selection/Selector.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/selection/Selector.java b/src/java/org/apache/cassandra/cql3/selection/Selector.java index 3ed773b..8c1e6b8 100644 --- a/src/java/org/apache/cassandra/cql3/selection/Selector.java +++ b/src/java/org/apache/cassandra/cql3/selection/Selector.java @@ -18,11 +18,13 @@ package org.apache.cassandra.cql3.selection; import java.nio.ByteBuffer; +import java.util.Collections; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.selection.Selection.ResultSetBuilder; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.exceptions.InvalidRequestException; @@ -45,6 +47,11 @@ public abstract class Selector implements AssignmentTestable return false; } + public Iterable<Function> getFunctions() + { + return Collections.emptySet(); + } + /** * Returns the column specification corresponding to the output value of the selector instances created by * this factory. @@ -167,4 +174,4 @@ public abstract class Selector implements AssignmentTestable else return AssignmentTestable.TestResult.NOT_ASSIGNABLE; } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java b/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java index 4e6970b..6de766a 100644 --- a/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java +++ b/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java @@ -17,15 +17,14 @@ */ package org.apache.cassandra.cql3.selection; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.*; -import com.google.common.base.Function; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; +import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.selection.Selector.Factory; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.exceptions.InvalidRequestException; @@ -98,6 +97,15 @@ final class SelectorFactories implements Iterable<Selector.Factory> return false; } + public Iterable<Function> getFunctions() + { + Iterable<Function> functions = Collections.emptySet(); + for (Factory factory : factories) + if (factory != null) + functions = Iterables.concat(functions, factory.getFunctions()); + return functions; + } + /** * Adds a new <code>Selector.Factory</code> for a column that is needed only for ORDER BY purposes. * @param def the column that is needed for ordering @@ -179,7 +187,7 @@ final class SelectorFactories implements Iterable<Selector.Factory> */ public List<String> getColumnNames() { - return Lists.transform(factories, new Function<Selector.Factory, String>() + return Lists.transform(factories, new com.google.common.base.Function<Selector.Factory, String>() { public String apply(Selector.Factory factory) { @@ -195,7 +203,7 @@ final class SelectorFactories implements Iterable<Selector.Factory> */ public List<AbstractType<?>> getReturnTypes() { - return Lists.transform(factories, new Function<Selector.Factory, AbstractType<?>>() + return Lists.transform(factories, new com.google.common.base.Function<Selector.Factory, AbstractType<?>>() { public AbstractType<?> apply(Selector.Factory factory) { http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb5897f3/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 84f35ff..3f873e7 100644 --- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java @@ -21,15 +21,12 @@ import java.nio.ByteBuffer; import java.util.*; import com.google.common.base.Function; -import com.google.common.collect.*; -import org.apache.cassandra.config.DatabaseDescriptor; - -import org.apache.cassandra.tracing.Tracing; - +import com.google.common.collect.Iterables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.config.ColumnDefinition; +import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.*; import org.apache.cassandra.db.*; import org.apache.cassandra.db.composites.Composite; @@ -37,6 +34,7 @@ import org.apache.cassandra.exceptions.*; import org.apache.cassandra.service.ClientState; import org.apache.cassandra.service.QueryState; import org.apache.cassandra.service.StorageProxy; +import org.apache.cassandra.tracing.Tracing; import org.apache.cassandra.transport.messages.ResultMessage; /** @@ -88,6 +86,14 @@ public class BatchStatement implements CQLStatement return false; } + public Iterable<org.apache.cassandra.cql3.functions.Function> getFunctions() + { + Iterable<org.apache.cassandra.cql3.functions.Function> functions = attrs.getFunctions(); + for (ModificationStatement statement : statements) + functions = Iterables.concat(functions, statement.getFunctions()); + return functions; + } + public int getBoundTerms() { return boundTerms;
