You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by al...@apache.org on 2015/06/30 14:04:34 UTC
[2/2] cassandra git commit: Improve handling of UDA and UDF metadata
Improve handling of UDA and UDF metadata
patch by Aleksey Yeschenko; reviewed by Robert Stupp for CASSANDRA-9665
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/35668435
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/35668435
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/35668435
Branch: refs/heads/trunk
Commit: 35668435090eb47cf8c5e704243510b6cee35a7b
Parents: 67db844
Author: Aleksey Yeschenko <al...@apache.org>
Authored: Tue Jun 30 15:02:10 2015 +0300
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Tue Jun 30 15:04:51 2015 +0300
----------------------------------------------------------------------
CHANGES.txt | 5 +-
.../apache/cassandra/auth/FunctionResource.java | 10 +-
.../org/apache/cassandra/config/KSMetaData.java | 43 ++-
.../org/apache/cassandra/config/Schema.java | 117 ++++--
.../apache/cassandra/cql3/QueryProcessor.java | 59 ++-
.../cassandra/cql3/functions/AggregateFcts.java | 49 ++-
.../cql3/functions/BytesConversionFcts.java | 26 +-
.../cassandra/cql3/functions/FunctionCall.java | 6 +-
.../cql3/functions/FunctionResolver.java | 201 ++++++++++
.../cassandra/cql3/functions/Functions.java | 386 -------------------
.../cassandra/cql3/functions/TimeFcts.java | 18 +
.../cassandra/cql3/functions/UDAggregate.java | 23 +-
.../cassandra/cql3/functions/UDFunction.java | 5 +-
.../cassandra/cql3/functions/UuidFcts.java | 8 +-
.../cassandra/cql3/selection/Selectable.java | 2 +-
.../statements/CreateAggregateStatement.java | 10 +-
.../statements/CreateFunctionStatement.java | 10 +-
.../cql3/statements/DropAggregateStatement.java | 12 +-
.../cql3/statements/DropFunctionStatement.java | 24 +-
.../cql3/statements/DropTypeStatement.java | 27 +-
.../org/apache/cassandra/db/SystemKeyspace.java | 27 +-
.../org/apache/cassandra/schema/Functions.java | 216 +++++++++++
.../cassandra/schema/LegacySchemaTables.java | 58 ++-
.../cassandra/service/MigrationManager.java | 6 +-
.../cql3/validation/entities/UFAuthTest.java | 6 +-
.../cql3/validation/entities/UFTest.java | 41 +-
.../validation/operations/AggregationTest.java | 15 +-
27 files changed, 804 insertions(+), 606 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 206b15d..aa3bdb4 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,4 @@
-3.0:
- * Improve log output from unit tests (CASSANDRA-9528)
+3.0
* Add algorithmic token allocation (CASSANDRA-7032)
* Add nodetool command to replay batchlog (CASSANDRA-9547)
* Make file buffer cache independent of paths being read (CASSANDRA-8897)
@@ -39,7 +38,7 @@ Merged from 2.1:
* Fix bug in cardinality check when compacting (CASSANDRA-9580)
* Fix memory leak in Ref due to ConcurrentLinkedQueue.remove() behaviour (CASSANDRA-9549)
* Make rebuild only run one at a time (CASSANDRA-9119)
-Merged from 2.0
+Merged from 2.0:
* Improve trace messages for RR (CASSANDRA-9479)
* Fix suboptimal secondary index selection when restricted
clustering column is also indexed (CASSANDRA-9631)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/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
index 1421541..2c5b8a1 100644
--- a/src/java/org/apache/cassandra/auth/FunctionResource.java
+++ b/src/java/org/apache/cassandra/auth/FunctionResource.java
@@ -19,6 +19,7 @@ package org.apache.cassandra.auth;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import com.google.common.base.Joiner;
@@ -31,7 +32,6 @@ 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;
@@ -244,7 +244,7 @@ public class FunctionResource implements IResource
case KEYSPACE:
return Schema.instance.getKeyspaces().contains(keyspace);
case FUNCTION:
- return Functions.find(getFunctionName(), argTypes) != null;
+ return Schema.instance.findFunction(getFunctionName(), argTypes).isPresent();
}
throw new AssertionError();
}
@@ -258,9 +258,9 @@ public class FunctionResource implements IResource
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;
+ Optional<Function> function = Schema.instance.findFunction(getFunctionName(), argTypes);
+ assert function.isPresent() : "Unable to find function object for resource " + toString();
+ return function.get().isAggregate() ? AGGREGATE_FUNCTION_PERMISSIONS : SCALAR_FUNCTION_PERMISSIONS;
}
}
throw new AssertionError();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/config/KSMetaData.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/KSMetaData.java b/src/java/org/apache/cassandra/config/KSMetaData.java
index 1537aae..a325a80 100644
--- a/src/java/org/apache/cassandra/config/KSMetaData.java
+++ b/src/java/org/apache/cassandra/config/KSMetaData.java
@@ -23,6 +23,7 @@ import com.google.common.base.Objects;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.locator.*;
+import org.apache.cassandra.schema.Functions;
import org.apache.cassandra.service.StorageService;
public final class KSMetaData
@@ -34,13 +35,14 @@ public final class KSMetaData
public final boolean durableWrites;
public final UTMetaData userTypes;
+ public final Functions functions;
public KSMetaData(String name,
Class<? extends AbstractReplicationStrategy> strategyClass,
Map<String, String> strategyOptions,
boolean durableWrites)
{
- this(name, strategyClass, strategyOptions, durableWrites, Collections.<CFMetaData>emptyList(), new UTMetaData());
+ this(name, strategyClass, strategyOptions, durableWrites, Collections.<CFMetaData>emptyList(), new UTMetaData(), Functions.none());
}
public KSMetaData(String name,
@@ -49,7 +51,17 @@ public final class KSMetaData
boolean durableWrites,
Iterable<CFMetaData> cfDefs)
{
- this(name, strategyClass, strategyOptions, durableWrites, cfDefs, new UTMetaData());
+ this(name, strategyClass, strategyOptions, durableWrites, cfDefs, new UTMetaData(), Functions.none());
+ }
+
+ public KSMetaData(String name,
+ Class<? extends AbstractReplicationStrategy> strategyClass,
+ Map<String, String> strategyOptions,
+ boolean durableWrites,
+ Iterable<CFMetaData> cfDefs,
+ Functions functions)
+ {
+ this(name, strategyClass, strategyOptions, durableWrites, cfDefs, new UTMetaData(), functions);
}
private KSMetaData(String name,
@@ -57,7 +69,8 @@ public final class KSMetaData
Map<String, String> strategyOptions,
boolean durableWrites,
Iterable<CFMetaData> cfDefs,
- UTMetaData userTypes)
+ UTMetaData userTypes,
+ Functions functions)
{
this.name = name;
this.strategyClass = strategyClass == null ? NetworkTopologyStrategy.class : strategyClass;
@@ -68,6 +81,7 @@ public final class KSMetaData
this.cfMetaData = Collections.unmodifiableMap(cfmap);
this.durableWrites = durableWrites;
this.userTypes = userTypes;
+ this.functions = functions;
}
// For new user created keyspaces (through CQL)
@@ -82,7 +96,7 @@ public final class KSMetaData
public static KSMetaData newKeyspace(String name, Class<? extends AbstractReplicationStrategy> strategyClass, Map<String, String> options, boolean durablesWrites, Iterable<CFMetaData> cfDefs)
{
- return new KSMetaData(name, strategyClass, options, durablesWrites, cfDefs, new UTMetaData());
+ return new KSMetaData(name, strategyClass, options, durablesWrites, cfDefs, new UTMetaData(), Functions.none());
}
public KSMetaData cloneWithTableRemoved(CFMetaData table)
@@ -91,7 +105,7 @@ public final class KSMetaData
List<CFMetaData> newTables = new ArrayList<>(cfMetaData().values());
newTables.remove(table);
assert newTables.size() == cfMetaData().size() - 1;
- return cloneWith(newTables, userTypes);
+ return cloneWith(newTables, userTypes, functions);
}
public KSMetaData cloneWithTableAdded(CFMetaData table)
@@ -100,12 +114,17 @@ public final class KSMetaData
List<CFMetaData> newTables = new ArrayList<>(cfMetaData().values());
newTables.add(table);
assert newTables.size() == cfMetaData().size() + 1;
- return cloneWith(newTables, userTypes);
+ return cloneWith(newTables, userTypes, functions);
}
- public KSMetaData cloneWith(Iterable<CFMetaData> tables, UTMetaData types)
+ public KSMetaData cloneWith(Iterable<CFMetaData> tables, UTMetaData types, Functions functions)
{
- return new KSMetaData(name, strategyClass, strategyOptions, durableWrites, tables, types);
+ return new KSMetaData(name, strategyClass, strategyOptions, durableWrites, tables, types, functions);
+ }
+
+ public KSMetaData cloneWith(Functions functions)
+ {
+ return new KSMetaData(name, strategyClass, strategyOptions, durableWrites, cfMetaData.values(), userTypes, functions);
}
public static KSMetaData testMetadata(String name, Class<? extends AbstractReplicationStrategy> strategyClass, Map<String, String> strategyOptions, CFMetaData... cfDefs)
@@ -121,7 +140,7 @@ public final class KSMetaData
@Override
public int hashCode()
{
- return Objects.hashCode(name, strategyClass, strategyOptions, cfMetaData, durableWrites, userTypes);
+ return Objects.hashCode(name, strategyClass, strategyOptions, cfMetaData, durableWrites, functions, userTypes);
}
@Override
@@ -140,6 +159,7 @@ public final class KSMetaData
&& Objects.equal(strategyOptions, other.strategyOptions)
&& Objects.equal(cfMetaData, other.cfMetaData)
&& Objects.equal(durableWrites, other.durableWrites)
+ && Objects.equal(functions, other.functions)
&& Objects.equal(userTypes, other.userTypes);
}
@@ -157,6 +177,7 @@ public final class KSMetaData
.add("strategyOptions", strategyOptions)
.add("cfMetaData", cfMetaData)
.add("durableWrites", durableWrites)
+ .add("functions", functions)
.add("userTypes", userTypes)
.toString();
}
@@ -176,9 +197,7 @@ public final class KSMetaData
IEndpointSnitch eps = DatabaseDescriptor.getEndpointSnitch();
AbstractReplicationStrategy.validateReplicationStrategy(name, strategyClass, tmd, eps, strategyOptions);
- for (CFMetaData cfm : cfMetaData.values())
- cfm.validate();
-
+ cfMetaData.values().forEach(CFMetaData::validate);
return this;
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/config/Schema.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/Schema.java b/src/java/org/apache/cassandra/config/Schema.java
index 2678cb3..07684ad 100644
--- a/src/java/org/apache/cassandra/config/Schema.java
+++ b/src/java/org/apache/cassandra/config/Schema.java
@@ -26,13 +26,15 @@ import com.google.common.collect.Sets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.apache.cassandra.cql3.functions.Functions;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.functions.FunctionName;
import org.apache.cassandra.cql3.functions.UDAggregate;
import org.apache.cassandra.cql3.functions.UDFunction;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.commitlog.CommitLog;
import org.apache.cassandra.db.compaction.CompactionManager;
+import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.io.sstable.Descriptor;
import org.apache.cassandra.schema.LegacySchemaTables;
@@ -283,14 +285,6 @@ public class Schema
}
/**
- * @return collection of the metadata about all keyspaces registered in the system (system and non-system)
- */
- public Collection<KSMetaData> getKeyspaceDefinitions()
- {
- return keyspaces.values();
- }
-
- /**
* Update (or insert) new keyspace definition
*
* @param ksm The metadata about keyspace
@@ -362,6 +356,45 @@ public class Schema
cfm.markPurged();
}
+ /* Function helpers */
+
+ /**
+ * Get all function overloads with the specified name
+ *
+ * @param name fully qualified function name
+ * @return an empty list if the keyspace or the function name are not found;
+ * a non-empty collection of {@link Function} otherwise
+ */
+ public Collection<Function> getFunctions(FunctionName name)
+ {
+ if (!name.hasKeyspace())
+ throw new IllegalArgumentException(String.format("Function name must be fully quallified: got %s", name));
+
+ KSMetaData ksm = getKSMetaData(name.keyspace);
+ return ksm == null
+ ? Collections.emptyList()
+ : ksm.functions.get(name);
+ }
+
+ /**
+ * Find the function with the specified name
+ *
+ * @param name fully qualified function name
+ * @param argTypes function argument types
+ * @return an empty {@link Optional} if the keyspace or the function name are not found;
+ * a non-empty optional of {@link Function} otherwise
+ */
+ public Optional<Function> findFunction(FunctionName name, List<AbstractType<?>> argTypes)
+ {
+ if (!name.hasKeyspace())
+ throw new IllegalArgumentException(String.format("Function name must be fully quallified: got %s", name));
+
+ KSMetaData ksm = getKSMetaData(name.keyspace);
+ return ksm == null
+ ? Optional.empty()
+ : ksm.functions.find(name, argTypes);
+ }
+
/* Version control */
/**
@@ -420,7 +453,7 @@ public class Schema
{
KSMetaData oldKsm = getKSMetaData(ksName);
assert oldKsm != null;
- KSMetaData newKsm = LegacySchemaTables.createKeyspaceFromName(ksName).cloneWith(oldKsm.cfMetaData().values(), oldKsm.userTypes);
+ KSMetaData newKsm = LegacySchemaTables.createKeyspaceFromName(ksName).cloneWith(oldKsm.cfMetaData().values(), oldKsm.userTypes, oldKsm.functions);
setKeyspaceDefinition(newKsm);
@@ -552,57 +585,67 @@ public class Schema
public void addFunction(UDFunction udf)
{
- logger.info("Loading {}", udf);
-
- Functions.addOrReplaceFunction(udf);
-
+ addFunctionInternal(udf);
MigrationManager.instance.notifyCreateFunction(udf);
}
public void updateFunction(UDFunction udf)
{
- logger.info("Updating {}", udf);
-
- Functions.addOrReplaceFunction(udf);
-
+ updateFunctionInternal(udf);
MigrationManager.instance.notifyUpdateFunction(udf);
}
public void dropFunction(UDFunction udf)
{
- logger.info("Drop {}", udf);
-
- // TODO: this is kind of broken as this remove all overloads of the function name
- Functions.removeFunction(udf.name(), udf.argTypes());
-
+ dropFunctionInternal(udf);
MigrationManager.instance.notifyDropFunction(udf);
}
- public void addAggregate(UDAggregate udf)
+ public void addAggregate(UDAggregate uda)
{
- logger.info("Loading {}", udf);
-
- Functions.addOrReplaceFunction(udf);
+ addFunctionInternal(uda);
+ MigrationManager.instance.notifyCreateAggregate(uda);
+ }
- MigrationManager.instance.notifyCreateAggregate(udf);
+ public void updateAggregate(UDAggregate uda)
+ {
+ updateFunctionInternal(uda);
+ MigrationManager.instance.notifyUpdateAggregate(uda);
}
- public void updateAggregate(UDAggregate udf)
+ public void dropAggregate(UDAggregate uda)
{
- logger.info("Updating {}", udf);
+ dropFunctionInternal(uda);
+ MigrationManager.instance.notifyDropAggregate(uda);
+ }
- Functions.addOrReplaceFunction(udf);
+ private void addFunctionInternal(Function fun)
+ {
+ assert fun instanceof UDFunction || fun instanceof UDAggregate;
- MigrationManager.instance.notifyUpdateAggregate(udf);
+ KSMetaData oldKsm = getKSMetaData(fun.name().keyspace);
+ assert oldKsm != null;
+ KSMetaData newKsm = oldKsm.cloneWith(oldKsm.functions.with(fun));
+ setKeyspaceDefinition(newKsm);
}
- public void dropAggregate(UDAggregate udf)
+ private void updateFunctionInternal(Function fun)
{
- logger.info("Drop {}", udf);
+ assert fun instanceof UDFunction || fun instanceof UDAggregate;
+
+ KSMetaData oldKsm = getKSMetaData(fun.name().keyspace);
+ assert oldKsm != null;
+ KSMetaData newKsm = oldKsm.cloneWith(oldKsm.functions.without(fun.name(), fun.argTypes()).with(fun));
+ setKeyspaceDefinition(newKsm);
+ }
- // TODO: this is kind of broken as this remove all overloads of the function name
- Functions.removeFunction(udf.name(), udf.argTypes());
+ private void dropFunctionInternal(Function fun)
+ {
+ assert fun instanceof UDFunction || fun instanceof UDAggregate;
- MigrationManager.instance.notifyDropAggregate(udf);
+ KSMetaData oldKsm = getKSMetaData(fun.name().keyspace);
+ assert oldKsm != null;
+ KSMetaData newKsm = oldKsm.cloneWith(oldKsm.functions.without(fun.name(), fun.argTypes()));
+ setKeyspaceDefinition(newKsm);
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/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 b369202..8db8554 100644
--- a/src/java/org/apache/cassandra/cql3/QueryProcessor.java
+++ b/src/java/org/apache/cassandra/cql3/QueryProcessor.java
@@ -36,9 +36,9 @@ import com.googlecode.concurrentlinkedhashmap.EntryWeigher;
import com.googlecode.concurrentlinkedhashmap.EvictionListener;
import org.antlr.runtime.*;
import org.apache.cassandra.concurrent.ScheduledExecutors;
+import org.apache.cassandra.config.Schema;
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.cql3.statements.*;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.rows.RowIterator;
@@ -602,24 +602,26 @@ public class QueryProcessor implements QueryHandler
return ksName.equals(statementKsName) && (cfName == null || cfName.equals(statementCfName));
}
- public void onCreateFunction(String ksName, String functionName, List<AbstractType<?>> argTypes) {
- if (Functions.getOverloadCount(new FunctionName(ksName, functionName)) > 1)
+ public void onCreateFunction(String ksName, String functionName, List<AbstractType<?>> argTypes)
+ {
+ onCreateFunctionInternal(ksName, functionName, argTypes);
+ }
+
+ public void onCreateAggregate(String ksName, String aggregateName, List<AbstractType<?>> argTypes)
+ {
+ onCreateFunctionInternal(ksName, aggregateName, argTypes);
+ }
+
+ private static void onCreateFunctionInternal(String ksName, String functionName, List<AbstractType<?>> argTypes)
+ {
+ // in case there are other overloads, we have to remove all overloads since argument type
+ // matching may change (due to type casting)
+ if (Schema.instance.getKSMetaData(ksName).functions.get(new FunctionName(ksName, functionName)).size() > 1)
{
- // in case there are other overloads, we have to remove all overloads since argument type
- // matching may change (due to type casting)
removeInvalidPreparedStatementsForFunction(preparedStatements.values().iterator(), ksName, functionName);
removeInvalidPreparedStatementsForFunction(thriftPreparedStatements.values().iterator(), ksName, functionName);
}
}
- public void onCreateAggregate(String ksName, String aggregateName, List<AbstractType<?>> argTypes) {
- if (Functions.getOverloadCount(new FunctionName(ksName, aggregateName)) > 1)
- {
- // in case there are other overloads, we have to remove all overloads since argument type
- // matching may change (due to type casting)
- removeInvalidPreparedStatementsForFunction(preparedStatements.values().iterator(), ksName, aggregateName);
- removeInvalidPreparedStatementsForFunction(thriftPreparedStatements.values().iterator(), ksName, aggregateName);
- }
- }
public void onUpdateColumnFamily(String ksName, String cfName, boolean columnsDidChange)
{
@@ -642,35 +644,26 @@ public class QueryProcessor implements QueryHandler
public void onDropFunction(String ksName, String functionName, List<AbstractType<?>> argTypes)
{
- removeInvalidPreparedStatementsForFunction(preparedStatements.values().iterator(), ksName, functionName);
- removeInvalidPreparedStatementsForFunction(thriftPreparedStatements.values().iterator(), ksName, functionName);
+ onDropFunctionInternal(ksName, functionName, argTypes);
}
public void onDropAggregate(String ksName, String aggregateName, List<AbstractType<?>> argTypes)
{
- removeInvalidPreparedStatementsForFunction(preparedStatements.values().iterator(), ksName, aggregateName);
- removeInvalidPreparedStatementsForFunction(thriftPreparedStatements.values().iterator(), ksName, aggregateName);
+ onDropFunctionInternal(ksName, aggregateName, argTypes);
+ }
+
+ private static void onDropFunctionInternal(String ksName, String functionName, List<AbstractType<?>> argTypes)
+ {
+ removeInvalidPreparedStatementsForFunction(preparedStatements.values().iterator(), ksName, functionName);
+ removeInvalidPreparedStatementsForFunction(thriftPreparedStatements.values().iterator(), ksName, functionName);
}
private static void removeInvalidPreparedStatementsForFunction(Iterator<ParsedStatement.Prepared> statements,
final String ksName,
final String functionName)
{
- final Predicate<Function> matchesFunction = new Predicate<Function>()
- {
- public boolean apply(Function f)
- {
- return ksName.equals(f.name().keyspace) && functionName.equals(f.name().name);
- }
- };
-
- Iterators.removeIf(statements, new Predicate<ParsedStatement.Prepared>()
- {
- public boolean apply(ParsedStatement.Prepared statement)
- {
- return Iterables.any(statement.statement.getFunctions(), matchesFunction);
- }
- });
+ Predicate<Function> matchesFunction = f -> ksName.equals(f.name().keyspace) && functionName.equals(f.name().name);
+ Iterators.removeIf(statements, statement -> Iterables.any(statement.statement.getFunctions(), matchesFunction));
}
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java b/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java
index 865dfbf..8c36864 100644
--- a/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java
+++ b/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java
@@ -20,21 +20,54 @@ package org.apache.cassandra.cql3.functions;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
-import org.apache.cassandra.db.marshal.AbstractType;
-import org.apache.cassandra.db.marshal.DecimalType;
-import org.apache.cassandra.db.marshal.DoubleType;
-import org.apache.cassandra.db.marshal.FloatType;
-import org.apache.cassandra.db.marshal.Int32Type;
-import org.apache.cassandra.db.marshal.IntegerType;
-import org.apache.cassandra.db.marshal.LongType;
+import org.apache.cassandra.cql3.CQL3Type;
+import org.apache.cassandra.db.marshal.*;
/**
* Factory methods for aggregate functions.
*/
public abstract class AggregateFcts
{
+ public static Collection<AggregateFunction> all()
+ {
+ Collection<AggregateFunction> functions = new ArrayList<>();
+
+ functions.add(countRowsFunction);
+
+ // sum for primitives
+ functions.add(sumFunctionForInt32);
+ functions.add(sumFunctionForLong);
+ functions.add(sumFunctionForFloat);
+ functions.add(sumFunctionForDouble);
+ functions.add(sumFunctionForDecimal);
+ functions.add(sumFunctionForVarint);
+
+ // avg for primitives
+ functions.add(avgFunctionForInt32);
+ functions.add(avgFunctionForLong);
+ functions.add(avgFunctionForFloat);
+ functions.add(avgFunctionForDouble);
+ functions.add(avgFunctionForDecimal);
+ functions.add(avgFunctionForVarint);
+
+ // count, max, and min for all standard types
+ for (CQL3Type type : CQL3Type.Native.values())
+ {
+ if (type != CQL3Type.Native.VARCHAR) // varchar and text both mapping to UTF8Type
+ {
+ functions.add(AggregateFcts.makeCountFunction(type.getType()));
+ functions.add(AggregateFcts.makeMaxFunction(type.getType()));
+ functions.add(AggregateFcts.makeMinFunction(type.getType()));
+ }
+ }
+
+ return functions;
+ }
+
/**
* The function used to count the number of rows of a result set. This function is called when COUNT(*) or COUNT(1)
* is specified.
@@ -55,7 +88,7 @@ public abstract class AggregateFcts
public ByteBuffer compute(int protocolVersion)
{
- return ((LongType) returnType()).decompose(Long.valueOf(count));
+ return ((LongType) returnType()).decompose(count);
}
public void addInput(int protocolVersion, List<ByteBuffer> values)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java b/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java
index ddb33fc..d9c6a52 100644
--- a/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java
+++ b/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java
@@ -18,8 +18,11 @@
package org.apache.cassandra.cql3.functions;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.BytesType;
import org.apache.cassandra.db.marshal.UTF8Type;
@@ -29,6 +32,27 @@ import org.apache.cassandra.serializers.MarshalException;
public abstract class BytesConversionFcts
{
+ public static Collection<Function> all()
+ {
+ Collection<Function> functions = new ArrayList<>();
+
+ // because text and varchar ends up being synonymous, our automatic makeToBlobFunction doesn't work
+ // for varchar, so we special case it below. We also skip blob for obvious reasons.
+ for (CQL3Type type : CQL3Type.Native.values())
+ {
+ if (type != CQL3Type.Native.VARCHAR && type != CQL3Type.Native.BLOB)
+ {
+ functions.add(makeToBlobFunction(type.getType()));
+ functions.add(makeFromBlobFunction(type.getType()));
+ }
+ }
+
+ functions.add(VarcharAsBlobFct);
+ functions.add(BlobAsVarcharFct);
+
+ return functions;
+ }
+
// Most of the XAsBlob and blobAsX functions are basically no-op since everything is
// bytes internally. They only "trick" the type system.
public static Function makeToBlobFunction(AbstractType<?> fromType)
@@ -74,7 +98,7 @@ public abstract class BytesConversionFcts
}
};
- public static final Function BlobAsVarcharFact = new NativeScalarFunction("blobasvarchar", UTF8Type.instance, BytesType.instance)
+ public static final Function BlobAsVarcharFct = new NativeScalarFunction("blobasvarchar", UTF8Type.instance, BytesType.instance)
{
public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters)
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/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 4f53c98..b25d079 100644
--- a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
+++ b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
@@ -123,7 +123,7 @@ public class FunctionCall extends Term.NonTerminal
public Term prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
{
- Function fun = Functions.get(keyspace, name, terms, receiver.ksName, receiver.cfName, receiver.type);
+ Function fun = FunctionResolver.get(keyspace, name, terms, receiver.ksName, receiver.cfName, receiver.type);
if (fun == null)
throw new InvalidRequestException(String.format("Unknown function %s called", name));
if (fun.isAggregate())
@@ -145,7 +145,7 @@ public class FunctionCall extends Term.NonTerminal
List<Term> parameters = new ArrayList<>(terms.size());
for (int i = 0; i < terms.size(); i++)
{
- Term t = terms.get(i).prepare(keyspace, Functions.makeArgSpec(receiver.ksName, receiver.cfName, scalarFun, i));
+ Term t = terms.get(i).prepare(keyspace, FunctionResolver.makeArgSpec(receiver.ksName, receiver.cfName, scalarFun, i));
parameters.add(t);
}
@@ -160,7 +160,7 @@ public class FunctionCall extends Term.NonTerminal
// later with a more helpful error message that if we were to return false here.
try
{
- Function fun = Functions.get(keyspace, name, terms, receiver.ksName, receiver.cfName, receiver.type);
+ Function fun = FunctionResolver.get(keyspace, name, terms, receiver.ksName, receiver.cfName, receiver.type);
// Because fromJson() can return whatever type the receiver is, we'll always get EXACT_MATCH. To
// handle potentially ambiguous function calls with fromJson() as an argument, always return
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/cql3/functions/FunctionResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/FunctionResolver.java b/src/java/org/apache/cassandra/cql3/functions/FunctionResolver.java
new file mode 100644
index 0000000..be2daae
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/functions/FunctionResolver.java
@@ -0,0 +1,201 @@
+/*
+ * 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.functions;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.cassandra.config.Schema;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+import static java.util.stream.Collectors.joining;
+
+public final class FunctionResolver
+{
+ private FunctionResolver()
+ {
+ }
+
+ // We special case the token function because that's the only function whose argument types actually
+ // depend on the table on which the function is called. Because it's the sole exception, it's easier
+ // to handle it as a special case.
+ private static final FunctionName TOKEN_FUNCTION_NAME = FunctionName.nativeFunction("token");
+
+ public static ColumnSpecification makeArgSpec(String receiverKs, String receiverCf, Function fun, int i)
+ {
+ return new ColumnSpecification(receiverKs,
+ receiverCf,
+ new ColumnIdentifier("arg" + i + '(' + fun.name().toString().toLowerCase() + ')', true),
+ fun.argTypes().get(i));
+ }
+
+ /**
+ * @param keyspace the current keyspace
+ * @param name the name of the function
+ * @param providedArgs the arguments provided for the function call
+ * @param receiverKs the receiver's keyspace
+ * @param receiverCf the receiver's table
+ * @param receiverType if the receiver type is known (during inserts, for example), this should be the type of
+ * the receiver
+ * @throws InvalidRequestException
+ */
+ public static Function get(String keyspace,
+ FunctionName name,
+ List<? extends AssignmentTestable> providedArgs,
+ String receiverKs,
+ String receiverCf,
+ AbstractType<?> receiverType)
+ throws InvalidRequestException
+ {
+ if (name.equalsNativeFunction(TOKEN_FUNCTION_NAME))
+ return new TokenFct(Schema.instance.getCFMetaData(receiverKs, receiverCf));
+
+ // The toJson() function can accept any type of argument, so instances of it are not pre-declared. Instead,
+ // we create new instances as needed while handling selectors (which is the only place that toJson() is supported,
+ // due to needing to know the argument types in advance).
+ if (name.equalsNativeFunction(ToJsonFct.NAME))
+ throw new InvalidRequestException("toJson() may only be used within the selection clause of SELECT statements");
+
+ // Similarly, we can only use fromJson when we know the receiver type (such as inserts)
+ if (name.equalsNativeFunction(FromJsonFct.NAME))
+ {
+ if (receiverType == null)
+ throw new InvalidRequestException("fromJson() cannot be used in the selection clause of a SELECT statement");
+ return FromJsonFct.getInstance(receiverType);
+ }
+
+ Collection<Function> candidates;
+ if (!name.hasKeyspace())
+ {
+ // function name not fully qualified
+ candidates = new ArrayList<>();
+ // add 'SYSTEM' (native) candidates
+ candidates.addAll(Schema.instance.getFunctions(name.asNativeFunction()));
+ // add 'current keyspace' candidates
+ candidates.addAll(Schema.instance.getFunctions(new FunctionName(keyspace, name.name)));
+ }
+ else
+ {
+ // function name is fully qualified (keyspace + name)
+ candidates = Schema.instance.getFunctions(name);
+ }
+
+ if (candidates.isEmpty())
+ return null;
+
+ // Fast path if there is only one choice
+ if (candidates.size() == 1)
+ {
+ Function fun = candidates.iterator().next();
+ validateTypes(keyspace, fun, providedArgs, receiverKs, receiverCf);
+ return fun;
+ }
+
+ List<Function> compatibles = null;
+ for (Function toTest : candidates)
+ {
+ AssignmentTestable.TestResult r = matchAguments(keyspace, toTest, providedArgs, receiverKs, receiverCf);
+ switch (r)
+ {
+ case EXACT_MATCH:
+ // We always favor exact matches
+ return toTest;
+ case WEAKLY_ASSIGNABLE:
+ if (compatibles == null)
+ compatibles = new ArrayList<>();
+ compatibles.add(toTest);
+ break;
+ }
+ }
+
+ if (compatibles == null || compatibles.isEmpty())
+ throw new InvalidRequestException(String.format("Invalid call to function %s, none of its type signatures match (known type signatures: %s)",
+ name, format(candidates)));
+
+ if (compatibles.size() > 1)
+ throw new InvalidRequestException(String.format("Ambiguous call to function %s (can be matched by following signatures: %s): use type casts to disambiguate",
+ name, format(compatibles)));
+
+ return compatibles.get(0);
+ }
+
+ // This method and matchArguments are somewhat duplicate, but this method allows us to provide more precise errors in the common
+ // case where there is no override for a given function. This is thus probably worth the minor code duplication.
+ private static void validateTypes(String keyspace,
+ Function fun,
+ List<? extends AssignmentTestable> providedArgs,
+ String receiverKs,
+ String receiverCf)
+ throws InvalidRequestException
+ {
+ if (providedArgs.size() != fun.argTypes().size())
+ throw new InvalidRequestException(String.format("Invalid number of arguments in call to function %s: %d required but %d provided", fun.name(), fun.argTypes().size(), providedArgs.size()));
+
+ for (int i = 0; i < providedArgs.size(); i++)
+ {
+ AssignmentTestable provided = providedArgs.get(i);
+
+ // If the concrete argument is a bind variables, it can have any type.
+ // We'll validate the actually provided value at execution time.
+ if (provided == null)
+ continue;
+
+ ColumnSpecification expected = makeArgSpec(receiverKs, receiverCf, fun, i);
+ if (!provided.testAssignment(keyspace, expected).isAssignable())
+ throw new InvalidRequestException(String.format("Type error: %s cannot be passed as argument %d of function %s of type %s", provided, i, fun.name(), expected.type.asCQL3Type()));
+ }
+ }
+
+ private static AssignmentTestable.TestResult matchAguments(String keyspace,
+ Function fun,
+ List<? extends AssignmentTestable> providedArgs,
+ String receiverKs,
+ String receiverCf)
+ {
+ if (providedArgs.size() != fun.argTypes().size())
+ return AssignmentTestable.TestResult.NOT_ASSIGNABLE;
+
+ // It's an exact match if all are exact match, but is not assignable as soon as any is non assignable.
+ AssignmentTestable.TestResult res = AssignmentTestable.TestResult.EXACT_MATCH;
+ for (int i = 0; i < providedArgs.size(); i++)
+ {
+ AssignmentTestable provided = providedArgs.get(i);
+ if (provided == null)
+ {
+ res = AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE;
+ continue;
+ }
+
+ ColumnSpecification expected = makeArgSpec(receiverKs, receiverCf, fun, i);
+ AssignmentTestable.TestResult argRes = provided.testAssignment(keyspace, expected);
+ if (argRes == AssignmentTestable.TestResult.NOT_ASSIGNABLE)
+ return AssignmentTestable.TestResult.NOT_ASSIGNABLE;
+ if (argRes == AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE)
+ res = AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE;
+ }
+ return res;
+ }
+
+ private static String format(Collection<Function> funs)
+ {
+ return funs.stream().map(Function::toString).collect(joining(", "));
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/cql3/functions/Functions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/Functions.java b/src/java/org/apache/cassandra/cql3/functions/Functions.java
deleted file mode 100644
index 018c35c..0000000
--- a/src/java/org/apache/cassandra/cql3/functions/Functions.java
+++ /dev/null
@@ -1,386 +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.functions;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import org.apache.cassandra.config.Schema;
-import org.apache.cassandra.cql3.*;
-import org.apache.cassandra.db.marshal.AbstractType;
-import org.apache.cassandra.exceptions.InvalidRequestException;
-import org.apache.cassandra.service.MigrationListener;
-import org.apache.cassandra.service.MigrationManager;
-
-public abstract class Functions
-{
- // We special case the token function because that's the only function whose argument types actually
- // depend on the table on which the function is called. Because it's the sole exception, it's easier
- // to handle it as a special case.
- private static final FunctionName TOKEN_FUNCTION_NAME = FunctionName.nativeFunction("token");
-
- private Functions() {}
-
- private static final ConcurrentMap<FunctionName, List<Function>> declared = new ConcurrentHashMap<>();
-
- static
- {
- declare(AggregateFcts.countRowsFunction);
- declare(TimeFcts.nowFct);
- declare(TimeFcts.minTimeuuidFct);
- declare(TimeFcts.maxTimeuuidFct);
- declare(TimeFcts.dateOfFct);
- declare(TimeFcts.unixTimestampOfFct);
- declare(TimeFcts.timeUuidtoDate);
- declare(TimeFcts.timeUuidToTimestamp);
- declare(TimeFcts.timeUuidToUnixTimestamp);
- declare(TimeFcts.timestampToDate);
- declare(TimeFcts.timestampToUnixTimestamp);
- declare(TimeFcts.dateToTimestamp);
- declare(TimeFcts.dateToUnixTimestamp);
- declare(UuidFcts.uuidFct);
-
- for (CQL3Type type : CQL3Type.Native.values())
- {
- // Note: because text and varchar ends up being synonymous, our automatic makeToBlobFunction doesn't work
- // for varchar, so we special case it below. We also skip blob for obvious reasons.
- if (type != CQL3Type.Native.VARCHAR && type != CQL3Type.Native.BLOB)
- {
- declare(BytesConversionFcts.makeToBlobFunction(type.getType()));
- declare(BytesConversionFcts.makeFromBlobFunction(type.getType()));
- }
- }
- declare(BytesConversionFcts.VarcharAsBlobFct);
- declare(BytesConversionFcts.BlobAsVarcharFact);
-
- for (CQL3Type type : CQL3Type.Native.values())
- {
- // special case varchar to avoid duplicating functions for UTF8Type
- if (type != CQL3Type.Native.VARCHAR)
- {
- declare(AggregateFcts.makeCountFunction(type.getType()));
- declare(AggregateFcts.makeMaxFunction(type.getType()));
- declare(AggregateFcts.makeMinFunction(type.getType()));
- }
- }
- declare(AggregateFcts.sumFunctionForInt32);
- declare(AggregateFcts.sumFunctionForLong);
- declare(AggregateFcts.sumFunctionForFloat);
- declare(AggregateFcts.sumFunctionForDouble);
- declare(AggregateFcts.sumFunctionForDecimal);
- declare(AggregateFcts.sumFunctionForVarint);
- declare(AggregateFcts.avgFunctionForInt32);
- declare(AggregateFcts.avgFunctionForLong);
- declare(AggregateFcts.avgFunctionForFloat);
- declare(AggregateFcts.avgFunctionForDouble);
- declare(AggregateFcts.avgFunctionForVarint);
- declare(AggregateFcts.avgFunctionForDecimal);
-
- MigrationManager.instance.register(new FunctionsMigrationListener());
- }
-
- private static void declare(Function fun)
- {
- synchronized (declared)
- {
- List<Function> functions = declared.get(fun.name());
- if (functions == null)
- {
- functions = new CopyOnWriteArrayList<>();
- List<Function> existing = declared.putIfAbsent(fun.name(), functions);
- if (existing != null)
- functions = existing;
- }
- functions.add(fun);
- }
- }
-
- public static ColumnSpecification makeArgSpec(String receiverKs, String receiverCf, Function fun, int i)
- {
- return new ColumnSpecification(receiverKs,
- receiverCf,
- new ColumnIdentifier("arg" + i + '(' + fun.name().toString().toLowerCase() + ')', true),
- fun.argTypes().get(i));
- }
-
- public static int getOverloadCount(FunctionName name)
- {
- return find(name).size();
- }
-
- /**
- * @param keyspace the current keyspace
- * @param name the name of the function
- * @param providedArgs the arguments provided for the function call
- * @param receiverKs the receiver's keyspace
- * @param receiverCf the receiver's table
- * @param receiverType if the receiver type is known (during inserts, for example), this should be the type of
- * the receiver
- * @throws InvalidRequestException
- */
- public static Function get(String keyspace,
- FunctionName name,
- List<? extends AssignmentTestable> providedArgs,
- String receiverKs,
- String receiverCf,
- AbstractType<?> receiverType)
- throws InvalidRequestException
- {
- if (name.equalsNativeFunction(TOKEN_FUNCTION_NAME))
- return new TokenFct(Schema.instance.getCFMetaData(receiverKs, receiverCf));
-
- // The toJson() function can accept any type of argument, so instances of it are not pre-declared. Instead,
- // we create new instances as needed while handling selectors (which is the only place that toJson() is supported,
- // due to needing to know the argument types in advance).
- if (name.equalsNativeFunction(ToJsonFct.NAME))
- throw new InvalidRequestException("toJson() may only be used within the selection clause of SELECT statements");
-
- // Similarly, we can only use fromJson when we know the receiver type (such as inserts)
- if (name.equalsNativeFunction(FromJsonFct.NAME))
- {
- if (receiverType == null)
- throw new InvalidRequestException("fromJson() cannot be used in the selection clause of a SELECT statement");
- return FromJsonFct.getInstance(receiverType);
- }
-
- List<Function> candidates;
- if (!name.hasKeyspace())
- {
- // function name not fully qualified
- candidates = new ArrayList<>();
- // add 'SYSTEM' (native) candidates
- candidates.addAll(find(name.asNativeFunction()));
- // add 'current keyspace' candidates
- candidates.addAll(find(new FunctionName(keyspace, name.name)));
- }
- else
- // function name is fully qualified (keyspace + name)
- candidates = find(name);
-
- if (candidates.isEmpty())
- return null;
-
- // Fast path if there is only one choice
- if (candidates.size() == 1)
- {
- Function fun = candidates.get(0);
- validateTypes(keyspace, fun, providedArgs, receiverKs, receiverCf);
- return fun;
- }
-
- List<Function> compatibles = null;
- for (Function toTest : candidates)
- {
- AssignmentTestable.TestResult r = matchAguments(keyspace, toTest, providedArgs, receiverKs, receiverCf);
- switch (r)
- {
- case EXACT_MATCH:
- // We always favor exact matches
- return toTest;
- case WEAKLY_ASSIGNABLE:
- if (compatibles == null)
- compatibles = new ArrayList<>();
- compatibles.add(toTest);
- break;
- }
- }
-
- if (compatibles == null || compatibles.isEmpty())
- throw new InvalidRequestException(String.format("Invalid call to function %s, none of its type signatures match (known type signatures: %s)",
- name, toString(candidates)));
-
- if (compatibles.size() > 1)
- throw new InvalidRequestException(String.format("Ambiguous call to function %s (can be matched by following signatures: %s): use type casts to disambiguate",
- name, toString(compatibles)));
-
- return compatibles.get(0);
- }
-
- public static List<Function> find(FunctionName name)
- {
- List<Function> functions = declared.get(name);
- return functions != null ? functions : Collections.<Function>emptyList();
- }
-
- public static Function find(FunctionName name, List<AbstractType<?>> argTypes)
- {
- assert name.hasKeyspace() : "function name not fully qualified";
- for (Function f : find(name))
- {
- if (typeEquals(f.argTypes(), argTypes))
- return f;
- }
- return null;
- }
-
- // This method and matchArguments are somewhat duplicate, but this method allows us to provide more precise errors in the common
- // case where there is no override for a given function. This is thus probably worth the minor code duplication.
- private static void validateTypes(String keyspace,
- Function fun,
- List<? extends AssignmentTestable> providedArgs,
- String receiverKs,
- String receiverCf)
- throws InvalidRequestException
- {
- if (providedArgs.size() != fun.argTypes().size())
- throw new InvalidRequestException(String.format("Invalid number of arguments in call to function %s: %d required but %d provided", fun.name(), fun.argTypes().size(), providedArgs.size()));
-
- for (int i = 0; i < providedArgs.size(); i++)
- {
- AssignmentTestable provided = providedArgs.get(i);
-
- // If the concrete argument is a bind variables, it can have any type.
- // We'll validate the actually provided value at execution time.
- if (provided == null)
- continue;
-
- ColumnSpecification expected = makeArgSpec(receiverKs, receiverCf, fun, i);
- if (!provided.testAssignment(keyspace, expected).isAssignable())
- throw new InvalidRequestException(String.format("Type error: %s cannot be passed as argument %d of function %s of type %s", provided, i, fun.name(), expected.type.asCQL3Type()));
- }
- }
-
- private static AssignmentTestable.TestResult matchAguments(String keyspace,
- Function fun,
- List<? extends AssignmentTestable> providedArgs,
- String receiverKs,
- String receiverCf)
- {
- if (providedArgs.size() != fun.argTypes().size())
- return AssignmentTestable.TestResult.NOT_ASSIGNABLE;
-
- // It's an exact match if all are exact match, but is not assignable as soon as any is non assignable.
- AssignmentTestable.TestResult res = AssignmentTestable.TestResult.EXACT_MATCH;
- for (int i = 0; i < providedArgs.size(); i++)
- {
- AssignmentTestable provided = providedArgs.get(i);
- if (provided == null)
- {
- res = AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE;
- continue;
- }
-
- ColumnSpecification expected = makeArgSpec(receiverKs, receiverCf, fun, i);
- AssignmentTestable.TestResult argRes = provided.testAssignment(keyspace, expected);
- if (argRes == AssignmentTestable.TestResult.NOT_ASSIGNABLE)
- return AssignmentTestable.TestResult.NOT_ASSIGNABLE;
- if (argRes == AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE)
- res = AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE;
- }
- return res;
- }
-
- private static String toString(List<Function> funs)
- {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < funs.size(); i++)
- {
- if (i > 0) sb.append(", ");
- sb.append(funs.get(i));
- }
- return sb.toString();
- }
-
- public static void addOrReplaceFunction(AbstractFunction fun)
- {
- // We shouldn't get there unless that function don't exist
- removeFunction(fun.name(), fun.argTypes());
- declare(fun);
- }
-
- // Same remarks than for addFunction
- public static void removeFunction(FunctionName name, List<AbstractType<?>> argTypes)
- {
- assert name.hasKeyspace() : "function name " + name + " not fully qualified";
- synchronized (declared)
- {
- List<Function> functions = find(name);
- for (int i = 0; i < functions.size(); i++)
- {
- Function f = functions.get(i);
- if (!typeEquals(f.argTypes(), argTypes))
- continue;
- assert !f.isNative();
- functions.remove(i);
- if (functions.isEmpty())
- declared.remove(name);
- return;
- }
- }
- }
-
- public static List<Function> getReferencesTo(Function old)
- {
- List<Function> references = new ArrayList<>();
- for (List<Function> functions : declared.values())
- for (Function function : functions)
- if (function.hasReferenceTo(old))
- references.add(function);
- return references;
- }
-
- public static Collection<Function> all()
- {
- List<Function> all = new ArrayList<>();
- for (List<Function> functions : declared.values())
- all.addAll(functions);
- return all;
- }
-
- /*
- * We need to compare the CQL3 representation of the type because comparing
- * the AbstractType will fail for example if a UDT has been changed.
- * Reason is that UserType.equals() takes the field names and types into account.
- * Example CQL sequence that would fail when comparing AbstractType:
- * CREATE TYPE foo ...
- * CREATE FUNCTION bar ( par foo ) RETURNS foo ...
- * ALTER TYPE foo ADD ...
- * or
- * ALTER TYPE foo ALTER ...
- * or
- * ALTER TYPE foo RENAME ...
- */
- public static boolean typeEquals(AbstractType<?> t1, AbstractType<?> t2)
- {
- return t1.asCQL3Type().toString().equals(t2.asCQL3Type().toString());
- }
-
- public static boolean typeEquals(List<AbstractType<?>> t1, List<AbstractType<?>> t2)
- {
- if (t1.size() != t2.size())
- return false;
- for (int i = 0; i < t1.size(); i ++)
- if (!typeEquals(t1.get(i), t2.get(i)))
- return false;
- return true;
- }
-
- private static class FunctionsMigrationListener extends MigrationListener
- {
- public void onUpdateUserType(String ksName, String typeName) {
- for (Function function : all())
- if (function instanceof UDFunction)
- ((UDFunction)function).userTypeUpdated(ksName, typeName);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java b/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java
index a4623cd..93d6d3b 100644
--- a/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java
+++ b/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java
@@ -18,9 +18,11 @@
package org.apache.cassandra.cql3.functions;
import java.nio.ByteBuffer;
+import java.util.Collection;
import java.util.Date;
import java.util.List;
+import com.google.common.collect.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,6 +35,22 @@ public abstract class TimeFcts
{
public static Logger logger = LoggerFactory.getLogger(TimeFcts.class);
+ public static Collection<Function> all()
+ {
+ return ImmutableList.of(nowFct,
+ minTimeuuidFct,
+ maxTimeuuidFct,
+ dateOfFct,
+ unixTimestampOfFct,
+ timeUuidtoDate,
+ timeUuidToTimestamp,
+ timeUuidToUnixTimestamp,
+ timestampToUnixTimestamp,
+ timestampToDate,
+ dateToUnixTimestamp,
+ dateToTimestamp);
+ }
+
public static final Function nowFct = new NativeScalarFunction("now", TimeUUIDType.instance)
{
public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/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 f153aed..aeee3ec 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java
@@ -25,8 +25,10 @@ import com.google.common.collect.ImmutableSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.apache.cassandra.config.Schema;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.schema.Functions;
/**
* Base class for user-defined-aggregates.
@@ -181,14 +183,19 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction
private static ScalarFunction resolveScalar(FunctionName aName, FunctionName fName, List<AbstractType<?>> argTypes) throws InvalidRequestException
{
- Function func = Functions.find(fName, argTypes);
- if (func == null)
+ Optional<Function> fun = Schema.instance.findFunction(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 (!(func instanceof ScalarFunction))
+ 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) func;
+ fName,
+ Arrays.toString(UDHelper.driverTypes(argTypes)),
+ aName));
+ return (ScalarFunction) fun.get();
}
@Override
@@ -199,8 +206,8 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction
UDAggregate that = (UDAggregate) o;
return Objects.equal(name, that.name)
- && Functions.typeEquals(argTypes, that.argTypes)
- && Functions.typeEquals(returnType, that.returnType)
+ && Functions.typesMatch(argTypes, that.argTypes)
+ && Functions.typesMatch(returnType, that.returnType)
&& Objects.equal(stateFunction, that.stateFunction)
&& Objects.equal(finalFunction, that.finalFunction)
&& Objects.equal(stateType, that.stateType)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/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 aa6d555..77e4afe 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
@@ -33,6 +33,7 @@ import org.apache.cassandra.config.Schema;
import org.apache.cassandra.cql3.*;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.schema.Functions;
import org.apache.cassandra.service.MigrationManager;
import org.apache.cassandra.utils.ByteBufferUtil;
@@ -267,8 +268,8 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
UDFunction that = (UDFunction)o;
return Objects.equal(name, that.name)
&& Objects.equal(argNames, that.argNames)
- && Functions.typeEquals(argTypes, that.argTypes)
- && Functions.typeEquals(returnType, that.returnType)
+ && Functions.typesMatch(argTypes, that.argTypes)
+ && Functions.typesMatch(returnType, that.returnType)
&& Objects.equal(language, that.language)
&& Objects.equal(body, that.body);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/cql3/functions/UuidFcts.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/UuidFcts.java b/src/java/org/apache/cassandra/cql3/functions/UuidFcts.java
index 0aa3ac4..32adbdc 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UuidFcts.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UuidFcts.java
@@ -18,14 +18,18 @@
package org.apache.cassandra.cql3.functions;
import java.nio.ByteBuffer;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
import org.apache.cassandra.db.marshal.UUIDType;
import org.apache.cassandra.serializers.UUIDSerializer;
public abstract class UuidFcts
{
+ public static Collection<Function> all()
+ {
+ return Collections.singleton(uuidFct);
+ }
+
public static final Function uuidFct = new NativeScalarFunction("uuid", UUIDType.instance)
{
public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/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 ee134ee..717fe7c 100644
--- a/src/java/org/apache/cassandra/cql3/selection/Selectable.java
+++ b/src/java/org/apache/cassandra/cql3/selection/Selectable.java
@@ -148,7 +148,7 @@ public abstract class Selectable
if (functionName.equalsNativeFunction(ToJsonFct.NAME))
fun = ToJsonFct.getInstance(factories.getReturnTypes());
else
- fun = Functions.get(cfm.ksName, functionName, factories.newInstances(), cfm.ksName, cfm.cfName, null);
+ fun = FunctionResolver.get(cfm.ksName, functionName, factories.newInstances(), cfm.ksName, cfm.cfName, null);
if (fun == null)
throw new InvalidRequestException(String.format("Unknown function '%s'", functionName));
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java
index 039993f..16d9fc5 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java
@@ -90,7 +90,7 @@ public final class CreateAggregateStatement extends SchemaAlteringStatement
List<AbstractType<?>> stateArgs = stateArguments(stateType, argTypes);
stateFunc = validateFunctionKeyspace(stateFunc, stateArgs);
- Function f = Functions.find(stateFunc, stateArgs);
+ Function f = Schema.instance.findFunction(stateFunc, stateArgs).orElse(null);
if (!(f instanceof ScalarFunction))
throw new InvalidRequestException("State function " + stateFuncSig(stateFunc, stateTypeRaw, argRawTypes) + " does not exist or is not a scalar function");
stateFunction = (ScalarFunction)f;
@@ -103,7 +103,7 @@ public final class CreateAggregateStatement extends SchemaAlteringStatement
{
List<AbstractType<?>> finalArgs = Collections.<AbstractType<?>>singletonList(stateType);
finalFunc = validateFunctionKeyspace(finalFunc, finalArgs);
- f = Functions.find(finalFunc, finalArgs);
+ f = Schema.instance.findFunction(finalFunc, finalArgs).orElse(null);
if (!(f instanceof ScalarFunction))
throw new InvalidRequestException("Final function " + finalFunc + '(' + stateTypeRaw + ") does not exist or is not a scalar function");
finalFunction = (ScalarFunction) f;
@@ -156,7 +156,7 @@ public final class CreateAggregateStatement extends SchemaAlteringStatement
{
// If state/final function has no keyspace, check SYSTEM keyspace before logged keyspace.
FunctionName nativeName = FunctionName.nativeFunction(func.name);
- if (Functions.find(nativeName, argTypes) != null)
+ if (Schema.instance.findFunction(nativeName, argTypes).isPresent())
return nativeName;
return new FunctionName(functionName.keyspace, func.name);
@@ -186,7 +186,7 @@ public final class CreateAggregateStatement extends SchemaAlteringStatement
public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException
{
- if (Functions.find(functionName, argTypes) != null && orReplace)
+ if (Schema.instance.findFunction(functionName, argTypes).isPresent() && orReplace)
state.ensureHasPermission(Permission.ALTER, FunctionResource.function(functionName.keyspace,
functionName.name,
argTypes));
@@ -219,7 +219,7 @@ public final class CreateAggregateStatement extends SchemaAlteringStatement
public boolean announceMigration(boolean isLocalOnly) throws RequestValidationException
{
- Function old = Functions.find(functionName, argTypes);
+ Function old = Schema.instance.findFunction(functionName, argTypes).orElse(null);
if (old != null)
{
if (ifNotExists)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java
index 77e41ed..5446c82 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java
@@ -29,6 +29,7 @@ import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.cql3.functions.*;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.schema.Functions;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.MigrationManager;
import org.apache.cassandra.service.QueryState;
@@ -120,7 +121,7 @@ public final class CreateFunctionStatement extends SchemaAlteringStatement
public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException
{
- if (Functions.find(functionName, argTypes) != null && orReplace)
+ if (Schema.instance.findFunction(functionName, argTypes).isPresent() && orReplace)
state.ensureHasPermission(Permission.ALTER, FunctionResource.function(functionName.keyspace,
functionName.name,
argTypes));
@@ -149,7 +150,7 @@ public final class CreateFunctionStatement extends SchemaAlteringStatement
public boolean announceMigration(boolean isLocalOnly) throws RequestValidationException
{
- Function old = Functions.find(functionName, argTypes);
+ Function old = Schema.instance.findFunction(functionName, argTypes).orElse(null);
if (old != null)
{
if (ifNotExists)
@@ -162,7 +163,7 @@ public final class CreateFunctionStatement extends SchemaAlteringStatement
throw new InvalidRequestException(String.format("Function %s can only be replaced with %s", old,
calledOnNullInput ? "CALLED ON NULL INPUT" : "RETURNS NULL ON NULL INPUT"));
- if (!Functions.typeEquals(old.returnType(), returnType))
+ if (!Functions.typesMatch(old.returnType(), returnType))
throw new InvalidRequestException(String.format("Cannot replace function %s, the new return type %s is not compatible with the return type %s of existing function",
functionName, returnType.asCQL3Type(), old.returnType().asCQL3Type()));
}
@@ -170,9 +171,6 @@ public final class CreateFunctionStatement extends SchemaAlteringStatement
this.udFunction = UDFunction.create(functionName, argNames, argTypes, returnType, calledOnNullInput, language, body);
this.replaced = old != null;
- // add function to registry to prevent duplicate compilation on coordinator during migration
- Functions.addOrReplaceFunction(udFunction);
-
MigrationManager.announceNewFunction(udFunction, isLocalOnly);
return true;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/cql3/statements/DropAggregateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/DropAggregateStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropAggregateStatement.java
index 2d5ea70..3aa176e 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropAggregateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropAggregateStatement.java
@@ -18,9 +18,11 @@
package org.apache.cassandra.cql3.statements;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.config.Schema;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.functions.*;
import org.apache.cassandra.db.marshal.AbstractType;
@@ -33,7 +35,7 @@ import org.apache.cassandra.thrift.ThriftValidation;
import org.apache.cassandra.transport.Event;
/**
- * A <code>DROP AGGREGATE</code> statement parsed from a CQL query.
+ * A {@code DROP AGGREGATE} statement parsed from a CQL query.
*/
public final class DropAggregateStatement extends SchemaAlteringStatement
{
@@ -85,7 +87,7 @@ public final class DropAggregateStatement extends SchemaAlteringStatement
public boolean announceMigration(boolean isLocalOnly) throws RequestValidationException
{
- List<Function> olds = Functions.find(functionName);
+ Collection<Function> olds = Schema.instance.getFunctions(functionName);
if (!argsPresent && olds != null && olds.size() > 1)
throw new InvalidRequestException(String.format("'DROP AGGREGATE %s' matches multiple function definitions; " +
@@ -101,7 +103,7 @@ public final class DropAggregateStatement extends SchemaAlteringStatement
Function old;
if (argsPresent)
{
- old = Functions.find(functionName, argTypes);
+ old = Schema.instance.findFunction(functionName, argTypes).orElse(null);
if (old == null || !(old instanceof AggregateFunction))
{
if (ifExists)
@@ -120,13 +122,13 @@ public final class DropAggregateStatement extends SchemaAlteringStatement
}
else
{
- if (olds == null || olds.isEmpty() || !(olds.get(0) instanceof AggregateFunction))
+ if (olds == null || olds.isEmpty() || !(olds.iterator().next() instanceof AggregateFunction))
{
if (ifExists)
return false;
throw new InvalidRequestException(String.format("Cannot drop non existing aggregate '%s'", functionName));
}
- old = olds.get(0);
+ old = olds.iterator().next();
}
if (old.isNative())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java
index 3957d97..d6d7925 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java
@@ -18,12 +18,15 @@
package org.apache.cassandra.cql3.statements;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import com.google.common.base.Joiner;
import org.apache.cassandra.auth.FunctionResource;
import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.config.KSMetaData;
+import org.apache.cassandra.config.Schema;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.functions.*;
import org.apache.cassandra.db.marshal.AbstractType;
@@ -36,7 +39,7 @@ import org.apache.cassandra.thrift.ThriftValidation;
import org.apache.cassandra.transport.Event;
/**
- * A <code>DROP FUNCTION</code> statement parsed from a CQL query.
+ * A {@code DROP FUNCTION} statement parsed from a CQL query.
*/
public final class DropFunctionStatement extends SchemaAlteringStatement
{
@@ -113,7 +116,7 @@ public final class DropFunctionStatement extends SchemaAlteringStatement
@Override
public void validate(ClientState state)
{
- List<Function> olds = Functions.find(functionName);
+ Collection<Function> olds = Schema.instance.getFunctions(functionName);
if (!argsPresent && olds != null && olds.size() > 1)
throw new InvalidRequestException(String.format("'DROP FUNCTION %s' matches multiple function definitions; " +
@@ -142,9 +145,10 @@ public final class DropFunctionStatement extends SchemaAlteringStatement
throw new InvalidRequestException(getMissingFunctionError());
}
- List<Function> references = Functions.getReferencesTo(old);
- if (!references.isEmpty())
- throw new InvalidRequestException(String.format("Function '%s' still referenced by %s", old, references));
+ KSMetaData ksm = Schema.instance.getKSMetaData(old.name().keyspace);
+ Collection<UDAggregate> referrers = ksm.functions.aggregatesUsingFunction(old);
+ if (!referrers.isEmpty())
+ throw new InvalidRequestException(String.format("Function '%s' still referenced by %s", old, referrers));
MigrationManager.announceFunctionDrop((UDFunction) old, isLocalOnly);
@@ -158,7 +162,7 @@ public final class DropFunctionStatement extends SchemaAlteringStatement
sb.append(functionName);
if (argsPresent)
sb.append(Joiner.on(", ").join(argRawTypes));
- sb.append("'");
+ sb.append('\'');
return sb.toString();
}
@@ -167,7 +171,7 @@ public final class DropFunctionStatement extends SchemaAlteringStatement
Function old;
if (argsPresent)
{
- old = Functions.find(functionName, argTypes);
+ old = Schema.instance.findFunction(functionName, argTypes).orElse(null);
if (old == null || !(old instanceof ScalarFunction))
{
return null;
@@ -175,11 +179,11 @@ public final class DropFunctionStatement extends SchemaAlteringStatement
}
else
{
- List<Function> olds = Functions.find(functionName);
- if (olds == null || olds.isEmpty() || !(olds.get(0) instanceof ScalarFunction))
+ Collection<Function> olds = Schema.instance.getFunctions(functionName);
+ if (olds == null || olds.isEmpty() || !(olds.iterator().next() instanceof ScalarFunction))
return null;
- old = olds.get(0);
+ old = olds.iterator().next();
}
return old;
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java
index 8ad4f6c..5edac58 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java
@@ -21,7 +21,6 @@ import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.config.*;
import org.apache.cassandra.cql3.*;
import org.apache.cassandra.cql3.functions.Function;
-import org.apache.cassandra.cql3.functions.Functions;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.exceptions.*;
import org.apache.cassandra.service.ClientState;
@@ -74,30 +73,24 @@ public class DropTypeStatement extends SchemaAlteringStatement
// we drop and 2) existing tables referencing the type (maybe in a nested
// way).
- for (Function function : Functions.all())
+ for (Function function : ksm.functions)
{
if (isUsedBy(function.returnType()))
throw new InvalidRequestException(String.format("Cannot drop user type %s as it is still used by function %s", name, function));
+
for (AbstractType<?> argType : function.argTypes())
if (isUsedBy(argType))
throw new InvalidRequestException(String.format("Cannot drop user type %s as it is still used by function %s", name, function));
}
- for (KSMetaData ksm2 : Schema.instance.getKeyspaceDefinitions())
- {
- for (UserType ut : ksm2.userTypes.getAllTypes().values())
- {
- if (ut.keyspace.equals(name.getKeyspace()) && ut.name.equals(name.getUserTypeName()))
- continue;
- if (isUsedBy(ut))
- throw new InvalidRequestException(String.format("Cannot drop user type %s as it is still used by user type %s", name, ut.asCQL3Type()));
- }
-
- for (CFMetaData cfm : ksm2.cfMetaData().values())
- for (ColumnDefinition def : cfm.allColumns())
- if (isUsedBy(def.type))
- throw new InvalidRequestException(String.format("Cannot drop user type %s as it is still used by table %s.%s", name, cfm.ksName, cfm.cfName));
- }
+ for (UserType ut : ksm.userTypes.getAllTypes().values())
+ if (!ut.name.equals(name.getUserTypeName()) && isUsedBy(ut))
+ throw new InvalidRequestException(String.format("Cannot drop user type %s as it is still used by user type %s", name, ut.asCQL3Type()));
+
+ for (CFMetaData cfm : ksm.cfMetaData().values())
+ for (ColumnDefinition def : cfm.allColumns())
+ if (isUsedBy(def.type))
+ throw new InvalidRequestException(String.format("Cannot drop user type %s as it is still used by table %s.%s", name, cfm.ksName, cfm.cfName));
}
private boolean isUsedBy(AbstractType<?> toCheck) throws RequestValidationException
http://git-wip-us.apache.org/repos/asf/cassandra/blob/35668435/src/java/org/apache/cassandra/db/SystemKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/SystemKeyspace.java b/src/java/org/apache/cassandra/db/SystemKeyspace.java
index 34c617f..1e15321 100644
--- a/src/java/org/apache/cassandra/db/SystemKeyspace.java
+++ b/src/java/org/apache/cassandra/db/SystemKeyspace.java
@@ -36,6 +36,7 @@ import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.KSMetaData;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.cql3.UntypedResultSet;
+import org.apache.cassandra.cql3.functions.*;
import org.apache.cassandra.db.partitions.*;
import org.apache.cassandra.db.commitlog.ReplayPosition;
import org.apache.cassandra.db.compaction.CompactionHistoryTabularData;
@@ -51,6 +52,7 @@ import org.apache.cassandra.locator.IEndpointSnitch;
import org.apache.cassandra.locator.LocalStrategy;
import org.apache.cassandra.metrics.RestorableMeter;
import org.apache.cassandra.net.MessagingService;
+import org.apache.cassandra.schema.Functions;
import org.apache.cassandra.schema.LegacySchemaTables;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.service.paxos.Commit;
@@ -64,6 +66,10 @@ import static org.apache.cassandra.cql3.QueryProcessor.executeOnceInternal;
public final class SystemKeyspace
{
+ private SystemKeyspace()
+ {
+ }
+
private static final Logger logger = LoggerFactory.getLogger(SystemKeyspace.class);
// Used to indicate that there was a previous version written to the legacy (pre 1.2)
@@ -246,11 +252,11 @@ public final class SystemKeyspace
private static final CFMetaData AvailableRanges =
compile(AVAILABLE_RANGES,
- "Available keyspace/ranges during bootstrap/replace that are ready to be served",
+ "available keyspace/ranges during bootstrap/replace that are ready to be served",
"CREATE TABLE %s ("
- + "keyspace_name text PRIMARY KEY,"
- + "ranges set<blob>"
- + ")");
+ + "keyspace_name text,"
+ + "ranges set<blob>,"
+ + "PRIMARY KEY ((keyspace_name)))");
private static CFMetaData compile(String name, String description, String schema)
{
@@ -275,7 +281,18 @@ public final class SystemKeyspace
SSTableActivity,
SizeEstimates,
AvailableRanges));
- return new KSMetaData(NAME, LocalStrategy.class, Collections.<String, String>emptyMap(), true, tables);
+
+ return new KSMetaData(NAME, LocalStrategy.class, Collections.<String, String>emptyMap(), true, tables, functions());
+ }
+
+ private static Functions functions()
+ {
+ return Functions.builder()
+ .add(UuidFcts.all())
+ .add(TimeFcts.all())
+ .add(BytesConversionFcts.all())
+ .add(AggregateFcts.all())
+ .build();
}
private static volatile Map<UUID, Pair<ReplayPosition, Long>> truncationRecords;