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;