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/05/03 23:29:03 UTC

cassandra git commit: Fix invalidation of prepared statements on function drops

Repository: cassandra
Updated Branches:
  refs/heads/trunk e28860b40 -> d9836e0ef


Fix invalidation of prepared statements on function drops

patch by Sam Tunnicliffe; reviewed by Aleksey Yeschenko for
CASSANDRA-9166


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/d9836e0e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/d9836e0e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/d9836e0e

Branch: refs/heads/trunk
Commit: d9836e0efe66c0cc0e33f32b605f6920893fa7a5
Parents: e28860b
Author: Sam Tunnicliffe <sa...@beobal.com>
Authored: Mon May 4 00:28:16 2015 +0300
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Mon May 4 00:28:16 2015 +0300

----------------------------------------------------------------------
 .../org/apache/cassandra/cql3/Attributes.java   |   6 -
 .../org/apache/cassandra/cql3/CQLStatement.java |   9 +-
 .../apache/cassandra/cql3/ColumnCondition.java  |  13 ---
 .../org/apache/cassandra/cql3/Operation.java    |   5 -
 .../apache/cassandra/cql3/QueryProcessor.java   |  73 ++++++------
 src/java/org/apache/cassandra/cql3/Term.java    |  15 ---
 .../org/apache/cassandra/cql3/UserTypes.java    |   9 --
 .../cql3/functions/AbstractFunction.java        |   5 -
 .../cassandra/cql3/functions/Function.java      |   2 -
 .../cassandra/cql3/functions/FunctionCall.java  |   5 -
 .../cassandra/cql3/functions/UDAggregate.java   |  12 +-
 .../cql3/restrictions/AbstractRestriction.java  |  36 +-----
 .../ForwardingPrimaryKeyRestrictions.java       |   6 -
 .../restrictions/MultiColumnRestriction.java    |  25 ----
 .../restrictions/PrimaryKeyRestrictionSet.java  |   6 -
 .../cql3/restrictions/Restriction.java          |  10 +-
 .../cql3/restrictions/RestrictionSet.java       |  10 --
 .../cql3/restrictions/Restrictions.java         |  10 +-
 .../restrictions/SingleColumnRestriction.java   |  39 +------
 .../restrictions/StatementRestrictions.java     |   7 --
 .../cql3/restrictions/TokenRestriction.java     |  13 ---
 .../selection/AbstractFunctionSelector.java     |   5 -
 .../cassandra/cql3/selection/Selection.java     |  11 --
 .../cassandra/cql3/selection/Selector.java      |   5 -
 .../cql3/selection/SelectorFactories.java       |   8 --
 .../cql3/statements/BatchStatement.java         |  10 --
 .../cql3/statements/ModificationStatement.java  |  28 -----
 .../cql3/statements/ParsedStatement.java        |   7 +-
 .../cql3/statements/SelectStatement.java        |   9 --
 .../org/apache/cassandra/cql3/CQLTester.java    |   4 +-
 .../cassandra/cql3/UFIdentificationTest.java    |   2 +-
 test/unit/org/apache/cassandra/cql3/UFTest.java | 117 +++++++++++++++++++
 32 files changed, 180 insertions(+), 342 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/Attributes.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Attributes.java b/src/java/org/apache/cassandra/cql3/Attributes.java
index f3d298b..7b38e9f 100644
--- a/src/java/org/apache/cassandra/cql3/Attributes.java
+++ b/src/java/org/apache/cassandra/cql3/Attributes.java
@@ -50,12 +50,6 @@ public class Attributes
         this.timeToLive = timeToLive;
     }
 
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        return (timestamp != null && timestamp.usesFunction(ksName, functionName))
-            || (timeToLive != null && timeToLive.usesFunction(ksName, functionName));
-    }
-
     public Iterable<Function> getFunctions()
     {
         if (timestamp != null && timeToLive != null)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/CQLStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/CQLStatement.java b/src/java/org/apache/cassandra/cql3/CQLStatement.java
index 80220a8..02292ad 100644
--- a/src/java/org/apache/cassandra/cql3/CQLStatement.java
+++ b/src/java/org/apache/cassandra/cql3/CQLStatement.java
@@ -54,13 +54,16 @@ public interface CQLStatement
     public ResultMessage execute(QueryState state, QueryOptions options) throws RequestValidationException, RequestExecutionException;
 
     /**
-     * Variante of execute used for internal query against the system tables, and thus only query the local node.
+     * Variant of execute used for internal query against the system tables, and thus only query the local node.
      *
      * @param state the current query state
      */
     public ResultMessage executeInternal(QueryState state, QueryOptions options) throws RequestValidationException, RequestExecutionException;
 
-    boolean usesFunction(String ksName, String functionName);
-
+    /**
+     * Return an Iterable over all of the functions (both native and user-defined) used by any component
+     * of the statement
+     * @return functions all functions found (may contain duplicates)
+     */
     public Iterable<Function> getFunctions();
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/ColumnCondition.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/ColumnCondition.java b/src/java/org/apache/cassandra/cql3/ColumnCondition.java
index acebfe7..c7b5ddb 100644
--- a/src/java/org/apache/cassandra/cql3/ColumnCondition.java
+++ b/src/java/org/apache/cassandra/cql3/ColumnCondition.java
@@ -97,19 +97,6 @@ public class ColumnCondition
         return new ColumnCondition(column, collectionElement, inMarker, null, Operator.IN);
     }
 
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        if (collectionElement != null && collectionElement.usesFunction(ksName, functionName))
-            return true;
-        if (value != null && value.usesFunction(ksName, functionName))
-            return true;
-        if (inValues != null)
-            for (Term value : inValues)
-                if (value != null && value.usesFunction(ksName, functionName))
-                    return true;
-        return false;
-    }
-
     public Iterable<Function> getFunctions()
     {
         Iterable<Function> iter = Collections.emptyList();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/Operation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Operation.java b/src/java/org/apache/cassandra/cql3/Operation.java
index d99a00d..4701a96 100644
--- a/src/java/org/apache/cassandra/cql3/Operation.java
+++ b/src/java/org/apache/cassandra/cql3/Operation.java
@@ -57,11 +57,6 @@ public abstract class Operation
         this.t = t;
     }
 
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        return t != null && t.usesFunction(ksName, functionName);
-    }
-
     public Iterable<Function> getFunctions()
     {
         return t != null ? t.getFunctions() : Collections.<Function>emptySet();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/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 f494436..2698a8f 100644
--- a/src/java/org/apache/cassandra/cql3/QueryProcessor.java
+++ b/src/java/org/apache/cassandra/cql3/QueryProcessor.java
@@ -18,52 +18,40 @@
 package org.apache.cassandra.cql3;
 
 import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.*;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
 import com.google.common.primitives.Ints;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
 import com.googlecode.concurrentlinkedhashmap.EntryWeigher;
 import com.googlecode.concurrentlinkedhashmap.EvictionListener;
-
 import org.antlr.runtime.*;
-import org.apache.cassandra.exceptions.CassandraException;
-import org.apache.cassandra.service.MigrationListener;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.apache.cassandra.concurrent.ScheduledExecutors;
-import org.apache.cassandra.cql3.functions.*;
-
+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.composites.CType;
-import org.apache.cassandra.db.composites.CellName;
-import org.apache.cassandra.db.composites.CellNameType;
-import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.db.composites.*;
 import org.apache.cassandra.db.marshal.AbstractType;
-import org.apache.cassandra.exceptions.InvalidRequestException;
-import org.apache.cassandra.exceptions.RequestExecutionException;
-import org.apache.cassandra.exceptions.RequestValidationException;
-import org.apache.cassandra.exceptions.SyntaxException;
+import org.apache.cassandra.exceptions.*;
 import org.apache.cassandra.metrics.CQLMetrics;
-import org.apache.cassandra.service.ClientState;
-import org.apache.cassandra.service.MigrationManager;
-import org.apache.cassandra.service.QueryState;
+import org.apache.cassandra.service.*;
 import org.apache.cassandra.service.pager.QueryPager;
 import org.apache.cassandra.service.pager.QueryPagers;
 import org.apache.cassandra.thrift.ThriftClientState;
 import org.apache.cassandra.tracing.Tracing;
 import org.apache.cassandra.transport.messages.ResultMessage;
-import org.apache.cassandra.utils.ByteBufferUtil;
-import org.apache.cassandra.utils.FBUtilities;
-import org.apache.cassandra.utils.MD5Digest;
-import org.apache.cassandra.utils.SemanticVersion;
+import org.apache.cassandra.utils.*;
 import org.github.jamm.MemoryMeter;
 
 public class QueryProcessor implements QueryHandler
@@ -628,22 +616,37 @@ public class QueryProcessor implements QueryHandler
             removeInvalidPreparedStatements(ksName, cfName);
         }
 
-        public void onDropFunction(String ksName, String functionName, List<AbstractType<?>> argTypes) {
+        public void onDropFunction(String ksName, String functionName, List<AbstractType<?>> argTypes)
+        {
             removeInvalidPreparedStatementsForFunction(preparedStatements.values().iterator(), ksName, functionName);
             removeInvalidPreparedStatementsForFunction(thriftPreparedStatements.values().iterator(), ksName, functionName);
         }
+
         public void onDropAggregate(String ksName, String aggregateName, List<AbstractType<?>> argTypes)
         {
             removeInvalidPreparedStatementsForFunction(preparedStatements.values().iterator(), ksName, aggregateName);
             removeInvalidPreparedStatementsForFunction(thriftPreparedStatements.values().iterator(), ksName, aggregateName);
         }
 
-        private static void removeInvalidPreparedStatementsForFunction(Iterator<ParsedStatement.Prepared> iterator,
-                                                                String ksName, String functionName)
+        private static void removeInvalidPreparedStatementsForFunction(Iterator<ParsedStatement.Prepared> statements,
+                                                                       final String ksName,
+                                                                       final String functionName)
         {
-            while (iterator.hasNext())
-                if (iterator.next().statement.usesFunction(ksName, functionName))
-                    iterator.remove();
+            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);
+                }
+            });
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/Term.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Term.java b/src/java/org/apache/cassandra/cql3/Term.java
index 6997f78..6fa0c76 100644
--- a/src/java/org/apache/cassandra/cql3/Term.java
+++ b/src/java/org/apache/cassandra/cql3/Term.java
@@ -70,8 +70,6 @@ public interface Term
      */
     public abstract boolean containsBindMarker();
 
-    boolean usesFunction(String ksName, String functionName);
-
     Iterable<Function> getFunctions();
 
     /**
@@ -122,11 +120,6 @@ public interface Term
         public void collectMarkerSpecification(VariableSpecifications boundNames) {}
         public Terminal bind(QueryOptions options) { return this; }
 
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return false;
-        }
-
         public Set<Function> getFunctions()
         {
             return Collections.emptySet();
@@ -168,14 +161,6 @@ public interface Term
      */
     public abstract class NonTerminal implements Term
     {
-        // TODO - this is not necessarily false, yet isn't overridden in concrete classes
-        // representing collection literals
-        // e,g "UPDATE table SET map_col = { key_function() : val_function() }) WHERE ....
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return false;
-        }
-
         public ByteBuffer bindAndGet(QueryOptions options) throws InvalidRequestException
         {
             Terminal t = bind(options);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/UserTypes.java b/src/java/org/apache/cassandra/cql3/UserTypes.java
index b6d8521..de3f545 100644
--- a/src/java/org/apache/cassandra/cql3/UserTypes.java
+++ b/src/java/org/apache/cassandra/cql3/UserTypes.java
@@ -150,15 +150,6 @@ public abstract class UserTypes
             this.values = values;
         }
 
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            if (values != null)
-                for (Term value : values)
-                    if (value != null && value.usesFunction(ksName, functionName))
-                        return true;
-            return false;
-        }
-
         public Iterable<Function> getFunctions()
         {
             return Terms.getFunctions(values);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/functions/AbstractFunction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/AbstractFunction.java b/src/java/org/apache/cassandra/cql3/functions/AbstractFunction.java
index 15ae757..1f5349e 100644
--- a/src/java/org/apache/cassandra/cql3/functions/AbstractFunction.java
+++ b/src/java/org/apache/cassandra/cql3/functions/AbstractFunction.java
@@ -67,11 +67,6 @@ public abstract class AbstractFunction implements Function
             && Objects.equal(this.returnType, that.returnType);
     }
 
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        return name.keyspace.equals(ksName) && name.name.equals(functionName);
-    }
-
     public Iterable<Function> getFunctions()
     {
         return ImmutableSet.<Function>of(this);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/functions/Function.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/Function.java b/src/java/org/apache/cassandra/cql3/functions/Function.java
index 3c2e5a7..6fb9ba2 100644
--- a/src/java/org/apache/cassandra/cql3/functions/Function.java
+++ b/src/java/org/apache/cassandra/cql3/functions/Function.java
@@ -50,8 +50,6 @@ public interface Function
      */
     public boolean isAggregate();
 
-    boolean usesFunction(String ksName, String functionName);
-
     Iterable<Function> getFunctions();
 
     boolean hasReferenceTo(Function function);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/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 f42a093..689da5a 100644
--- a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
+++ b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
@@ -41,11 +41,6 @@ public class FunctionCall extends Term.NonTerminal
         this.terms = terms;
     }
 
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        return fun.usesFunction(ksName, functionName);
-    }
-
     public Iterable<Function> getFunctions()
     {
         return Iterables.concat(Terms.getFunctions(terms), fun.getFunctions());

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/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 59f8daa..7eed4f0 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java
@@ -98,16 +98,12 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction
         return stateFunction == function || finalFunction == function;
     }
 
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        return super.usesFunction(ksName, functionName)
-            || stateFunction != null && stateFunction.name().keyspace.equals(ksName) && stateFunction.name().name.equals(functionName)
-            || finalFunction != null && finalFunction.name().keyspace.equals(ksName) && finalFunction.name().name.equals(functionName);
-    }
-
     public Iterable<Function> getFunctions()
     {
-        return ImmutableSet.of(this, stateFunction, finalFunction);
+        if (finalFunction != null)
+            return ImmutableSet.of(this, stateFunction, finalFunction);
+        else
+            return ImmutableSet.of(this, stateFunction);
     }
 
     public boolean isAggregate()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
index d36162c..64c94f4 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
@@ -18,18 +18,16 @@
 package org.apache.cassandra.cql3.restrictions;
 
 import java.nio.ByteBuffer;
-import java.util.List;
 
 import org.apache.cassandra.cql3.ColumnSpecification;
 import org.apache.cassandra.cql3.QueryOptions;
-import org.apache.cassandra.cql3.Term;
 import org.apache.cassandra.cql3.statements.Bound;
 import org.apache.cassandra.db.composites.CompositesBuilder;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet;
 import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
 import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
-import static org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet;
 
 /**
  * Base class for <code>Restriction</code>s
@@ -99,34 +97,4 @@ abstract class AbstractRestriction  implements Restriction
         checkFalse(value.remaining() > 0xFFFF, "Index expression values may not be larger than 64K");
         return value;
     }
-
-    /**
-     * Checks if the specified term is using the specified function.
-     *
-     * @param term the term to check
-     * @param ksName the function keyspace name
-     * @param functionName the function name
-     * @return <code>true</code> if the specified term is using the specified function, <code>false</code> otherwise.
-     */
-    protected static final boolean usesFunction(Term term, String ksName, String functionName)
-    {
-        return term != null && term.usesFunction(ksName, functionName);
-    }
-
-    /**
-     * Checks if one of the specified term is using the specified function.
-     *
-     * @param terms the terms to check
-     * @param ksName the function keyspace name
-     * @param functionName the function name
-     * @return <code>true</code> if onee of the specified term is using the specified function, <code>false</code> otherwise.
-     */
-    protected static final boolean usesFunction(List<Term> terms, String ksName, String functionName)
-    {
-        if (terms != null)
-            for (Term value : terms)
-                if (usesFunction(value, ksName, functionName))
-                    return true;
-        return false;
-    }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
index fd40b7a..03c6cbc 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
@@ -45,12 +45,6 @@ abstract class ForwardingPrimaryKeyRestrictions implements PrimaryKeyRestriction
     protected abstract PrimaryKeyRestrictions getDelegate();
 
     @Override
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        return getDelegate().usesFunction(ksName, functionName);
-    }
-
-    @Override
     public Iterable<Function> getFunctions()
     {
         return getDelegate().getFunctions();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
index 6d3ffab..c4bce4c 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
@@ -135,12 +135,6 @@ public abstract class MultiColumnRestriction extends AbstractRestriction
         }
 
         @Override
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return usesFunction(value, ksName, functionName);
-        }
-
-        @Override
         public Iterable<Function> getFunctions()
         {
             return value.getFunctions();
@@ -270,12 +264,6 @@ public abstract class MultiColumnRestriction extends AbstractRestriction
         }
 
         @Override
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return usesFunction(values, ksName, functionName);
-        }
-
-        @Override
         public Iterable<Function> getFunctions()
         {
             return Terms.getFunctions(values);
@@ -315,12 +303,6 @@ public abstract class MultiColumnRestriction extends AbstractRestriction
         }
 
         @Override
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return false;
-        }
-
-        @Override
         public Iterable<Function> getFunctions()
         {
             return Collections.emptySet();
@@ -395,13 +377,6 @@ public abstract class MultiColumnRestriction extends AbstractRestriction
         }
 
         @Override
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return (slice.hasBound(Bound.START) && usesFunction(slice.bound(Bound.START), ksName, functionName))
-                    || (slice.hasBound(Bound.END) && usesFunction(slice.bound(Bound.END), ksName, functionName));
-        }
-
-        @Override
         public Iterable<Function> getFunctions()
         {
             return slice.getFunctions();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
index ab61628..b49d774 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
@@ -141,12 +141,6 @@ final class PrimaryKeyRestrictionSet extends AbstractPrimaryKeyRestrictions
     }
 
     @Override
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        return restrictions.usesFunction(ksName, functionName);
-    }
-
-    @Override
     public Iterable<Function> getFunctions()
     {
         return restrictions.getFunctions();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
index 71dc373..c115a3b 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
@@ -63,14 +63,10 @@ public interface Restriction
     public Collection<ColumnDefinition> getColumnDefs();
 
     /**
-     * Returns <code>true</code> if one of the restrictions use the specified function.
-     *
-     * @param ksName the keyspace name
-     * @param functionName the function name
-     * @return <code>true</code> if one of the restrictions use the specified function, <code>false</code> otherwise.
+     * Return an Iterable over all of the functions (both native and user-defined) used by any component
+     * of the restriction
+     * @return functions all functions found (may contain duplicates)
      */
-    public boolean usesFunction(String ksName, String functionName);
-
     public Iterable<Function> getFunctions();
 
     /**

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
index 3a236cc..6bf7666 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
@@ -81,16 +81,6 @@ final class RestrictionSet implements Restrictions, Iterable<Restriction>
     }
 
     @Override
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        for (Restriction restriction : restrictions.values())
-            if (restriction.usesFunction(ksName, functionName))
-                return true;
-
-        return false;
-    }
-
-    @Override
     public Iterable<Function> getFunctions()
     {
         com.google.common.base.Function<Restriction, Iterable<Function>> transform =

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java
index 7487734..ab81bf7 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java
@@ -39,14 +39,10 @@ interface Restrictions
     public Collection<ColumnDefinition> getColumnDefs();
 
     /**
-     * Returns <code>true</code> if one of the restrictions use the specified function.
-     *
-     * @param ksName the keyspace name
-     * @param functionName the function name
-     * @return <code>true</code> if one of the restrictions use the specified function, <code>false</code> otherwise.
+     * Return an Iterable over all of the functions (both native and user-defined) used by any component
+     * of the restrictions
+     * @return functions all functions found (may contain duplicates)
      */
-    public boolean usesFunction(String ksName, String functionName);
-
     public Iterable<Function> getFunctions();
 
     /**

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
index 69ef5d2..d32f585 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
@@ -23,12 +23,10 @@ import java.util.*;
 import com.google.common.collect.Iterables;
 
 import org.apache.cassandra.config.ColumnDefinition;
-
 import org.apache.cassandra.cql3.*;
 import org.apache.cassandra.cql3.Term.Terminal;
 import org.apache.cassandra.cql3.functions.Function;
 import org.apache.cassandra.cql3.statements.Bound;
-
 import org.apache.cassandra.db.IndexExpression;
 import org.apache.cassandra.db.composites.CompositesBuilder;
 import org.apache.cassandra.db.index.SecondaryIndex;
@@ -36,12 +34,11 @@ import org.apache.cassandra.db.index.SecondaryIndexManager;
 import org.apache.cassandra.db.marshal.CompositeType;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 
-import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
-
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet;
 import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
 import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
 import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
-import static org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet;
 
 public abstract class SingleColumnRestriction extends AbstractRestriction
 {
@@ -111,12 +108,6 @@ public abstract class SingleColumnRestriction extends AbstractRestriction
         }
 
         @Override
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return usesFunction(value, ksName, functionName);
-        }
-
-        @Override
         public Iterable getFunctions()
         {
             return value.getFunctions();
@@ -225,12 +216,6 @@ public abstract class SingleColumnRestriction extends AbstractRestriction
         }
 
         @Override
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return usesFunction(values, ksName, functionName);
-        }
-
-        @Override
         public Iterable<Function> getFunctions()
         {
             return Terms.getFunctions(values);
@@ -263,12 +248,6 @@ public abstract class SingleColumnRestriction extends AbstractRestriction
         }
 
         @Override
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return false;
-        }
-
-        @Override
         public Iterable<Function> getFunctions()
         {
             return Collections.emptySet();
@@ -302,13 +281,6 @@ public abstract class SingleColumnRestriction extends AbstractRestriction
         }
 
         @Override
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return (slice.hasBound(Bound.START) && usesFunction(slice.bound(Bound.START), ksName, functionName))
-                    || (slice.hasBound(Bound.END) && usesFunction(slice.bound(Bound.END), ksName, functionName));
-        }
-
-        @Override
         public Iterable<Function> getFunctions()
         {
             return slice.getFunctions();
@@ -509,13 +481,6 @@ public abstract class SingleColumnRestriction extends AbstractRestriction
         }
 
         @Override
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return usesFunction(values, ksName, functionName) || usesFunction(keys, ksName, functionName) ||
-                   usesFunction(entryKeys, ksName, functionName) || usesFunction(entryValues, ksName, functionName);
-        }
-
-        @Override
         public Iterable<Function> getFunctions()
         {
             return Iterables.concat(Terms.getFunctions(values),

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
index dcaad47..f848e2e 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
@@ -173,13 +173,6 @@ public final class StatementRestrictions
             addSingleColumnRestriction((SingleColumnRestriction) restriction);
     }
 
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        return  partitionKeyRestrictions.usesFunction(ksName, functionName)
-                || clusteringColumnsRestrictions.usesFunction(ksName, functionName)
-                || nonPrimaryKeyRestrictions.usesFunction(ksName, functionName);
-    }
-
     public Iterable<Function> getFunctions()
     {
         return Iterables.concat(partitionKeyRestrictions.getFunctions(),

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
index 5a00bd4..f8cd0dc 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
@@ -173,12 +173,6 @@ public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions
         }
 
         @Override
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return usesFunction(value, ksName, functionName);
-        }
-
-        @Override
         public Iterable<Function> getFunctions()
         {
             return value.getFunctions();
@@ -233,13 +227,6 @@ public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions
         }
 
         @Override
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return (slice.hasBound(Bound.START) && usesFunction(slice.bound(Bound.START), ksName, functionName))
-                    || (slice.hasBound(Bound.END) && usesFunction(slice.bound(Bound.END), ksName, functionName));
-        }
-
-        @Override
         public Iterable<Function> getFunctions()
         {
             return slice.getFunctions();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java b/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java
index 11e7e48..1dd1903 100644
--- a/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java
+++ b/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java
@@ -68,11 +68,6 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
                 return fun.returnType();
             }
 
-            public boolean usesFunction(String ksName, String functionName)
-            {
-                return fun.usesFunction(ksName, functionName);
-            }
-
             public Iterable<Function> getFunctions()
             {
                 return Iterables.concat(fun.getFunctions(), factories.getFunctions());

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/selection/Selection.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/selection/Selection.java b/src/java/org/apache/cassandra/cql3/selection/Selection.java
index 25bce78..9c990ce 100644
--- a/src/java/org/apache/cassandra/cql3/selection/Selection.java
+++ b/src/java/org/apache/cassandra/cql3/selection/Selection.java
@@ -174,11 +174,6 @@ public abstract class Selection
         return columns.size() - 1;
     }
 
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        return false;
-    }
-
     public Iterable<Function> getFunctions()
     {
         return Collections.emptySet();
@@ -483,12 +478,6 @@ public abstract class Selection
         }
 
         @Override
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return factories.usesFunction(ksName, functionName);
-        }
-
-        @Override
         public Iterable<Function> getFunctions()
         {
             return factories.getFunctions();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/selection/Selector.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/selection/Selector.java b/src/java/org/apache/cassandra/cql3/selection/Selector.java
index 8c1e6b8..2d345de 100644
--- a/src/java/org/apache/cassandra/cql3/selection/Selector.java
+++ b/src/java/org/apache/cassandra/cql3/selection/Selector.java
@@ -42,11 +42,6 @@ public abstract class Selector implements AssignmentTestable
      */
     public static abstract class Factory
     {
-        public boolean usesFunction(String ksName, String functionName)
-        {
-            return false;
-        }
-
         public Iterable<Function> getFunctions()
         {
             return Collections.emptySet();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java b/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java
index 6de766a..beb7399 100644
--- a/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java
+++ b/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java
@@ -89,14 +89,6 @@ final class SelectorFactories implements Iterable<Selector.Factory>
         }
     }
 
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        for (Factory factory : factories)
-            if (factory != null && factory.usesFunction(ksName, functionName))
-                return true;
-        return false;
-    }
-
     public Iterable<Function> getFunctions()
     {
         Iterable<Function> functions = Collections.emptySet();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
index 27a6ffb..465b2d9 100644
--- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
@@ -76,16 +76,6 @@ public class BatchStatement implements CQLStatement
         this.hasConditions = hasConditions;
     }
 
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        if (attrs.usesFunction(ksName, functionName))
-            return true;
-        for (ModificationStatement statement : statements)
-            if (statement.usesFunction(ksName, functionName))
-                return true;
-        return false;
-    }
-
     public Iterable<org.apache.cassandra.cql3.functions.Function> getFunctions()
     {
         Iterable<org.apache.cassandra.cql3.functions.Function> functions = attrs.getFunctions();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index e7a1a20..0862a9f 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@ -20,7 +20,6 @@ package org.apache.cassandra.cql3.statements;
 import java.nio.ByteBuffer;
 import java.util.*;
 
-import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
@@ -95,33 +94,6 @@ public abstract class ModificationStatement implements CQLStatement
         this.attrs = attrs;
     }
 
-    public boolean usesFunction(String ksName, final String functionName)
-    {
-        if (attrs.usesFunction(ksName, functionName))
-            return true;
-
-        for (Restriction restriction : processedKeys.values())
-            if (restriction.usesFunction(ksName, functionName))
-                return true;
-
-        if (columnOperations != null)
-            for (Operation operation : columnOperations)
-                if (operation.usesFunction(ksName, functionName))
-                    return true;
-
-        if (columnConditions != null)
-            for (ColumnCondition condition : columnConditions)
-                if (condition.usesFunction(ksName, functionName))
-                    return true;
-
-        if (staticConditions != null)
-            for (ColumnCondition condition : staticConditions)
-                if (condition.usesFunction(ksName, functionName))
-                    return true;
-
-        return false;
-    }
-
     public Iterable<Function> getFunctions()
     {
         Iterable<Function> functions = attrs.getFunctions();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java b/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java
index b848df4..539a957 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java
@@ -65,12 +65,7 @@ public abstract class ParsedStatement
         }
     }
 
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        return false;
-    }
-
-    public List<Function> getFunctions()
+    public Iterable<Function> getFunctions()
     {
         return Collections.emptyList();
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 8f7f75e..fe473aa 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -106,15 +106,6 @@ public class SelectStatement implements CQLStatement
         this.limit = limit;
     }
 
-    @Override
-    public boolean usesFunction(String ksName, String functionName)
-    {
-        return selection.usesFunction(ksName, functionName)
-                || restrictions.usesFunction(ksName, functionName)
-                || (limit != null && limit.usesFunction(ksName, functionName));
-    }
-
-    @Override
     public Iterable<Function> getFunctions()
     {
         return Iterables.concat(selection.getFunctions(),

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java
index b2135aa..c318717 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -303,7 +303,9 @@ public abstract class CQLTester
     private static boolean containsAny(String filename, List<String> tables)
     {
         for (int i = 0, m = tables.size(); i < m; i++)
-            if (filename.contains(tables.get(i)))
+            // don't accidentally delete in-use directories with the
+            // same prefix as a table to delete, i.e. table_1 & table_11
+            if (filename.contains(tables.get(i) + "-"))
                 return true;
         return false;
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/test/unit/org/apache/cassandra/cql3/UFIdentificationTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/UFIdentificationTest.java b/test/unit/org/apache/cassandra/cql3/UFIdentificationTest.java
index cfa42b3..044c98b 100644
--- a/test/unit/org/apache/cassandra/cql3/UFIdentificationTest.java
+++ b/test/unit/org/apache/cassandra/cql3/UFIdentificationTest.java
@@ -158,7 +158,7 @@ public class UFIdentificationTest extends CQLTester
 
     @Test @Ignore
     // Technically, attributes like timestamp and ttl are Terms so could potentially
-    // resolve to function calls (& so you can call usesFunction & getFunctions on them)
+    // resolve to function calls (& so you can call getFunctions on them)
     // However, this is currently disallowed by CQL syntax
     public void testModificationStatementWithAttributesFromFunction() throws Throwable
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d9836e0e/test/unit/org/apache/cassandra/cql3/UFTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/UFTest.java b/test/unit/org/apache/cassandra/cql3/UFTest.java
index 3c6baf8..42bddf3 100644
--- a/test/unit/org/apache/cassandra/cql3/UFTest.java
+++ b/test/unit/org/apache/cassandra/cql3/UFTest.java
@@ -29,6 +29,7 @@ import com.datastax.driver.core.*;
 import org.apache.cassandra.cql3.functions.FunctionName;
 import org.apache.cassandra.cql3.functions.Functions;
 import org.apache.cassandra.cql3.functions.UDFunction;
+import org.apache.cassandra.db.marshal.CollectionType;
 import org.apache.cassandra.exceptions.FunctionExecutionException;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.service.ClientState;
@@ -176,6 +177,122 @@ public class UFTest extends CQLTester
     }
 
     @Test
+    public void testDropFunctionDropsPreparedStatementsWithDelayedValues() throws Throwable
+    {
+        // test that dropping a function removes stmts which use
+        // it to provide a DelayedValue collection from the
+        // cache in QueryProcessor
+        checkDelayedValuesCorrectlyIdentifyFunctionsInUse(false);
+    }
+
+    @Test
+    public void testDropKeyspaceContainingFunctionDropsPreparedStatementsWithDelayedValues() throws Throwable
+    {
+        // test that dropping a function removes stmts which use
+        // it to provide a DelayedValue collection from the
+        // cache in QueryProcessor
+        checkDelayedValuesCorrectlyIdentifyFunctionsInUse(true);
+    }
+
+    private ResultMessage.Prepared prepareStatementWithDelayedValue(CollectionType.Kind kind, String function)
+    {
+        String collectionType;
+        String literalArgs;
+        switch (kind)
+        {
+            case LIST:
+                collectionType = "list<double>";
+                literalArgs = String.format("[%s(0.0)]", function);
+                break;
+            case SET:
+                collectionType = "set<double>";
+                literalArgs = String.format("{%s(0.0)}", function);
+                break;
+            case MAP:
+                collectionType = "map<double, double>";
+                literalArgs = String.format("{%s(0.0):0.0}", function);
+                break;
+            default:
+                Assert.fail("Unsupported collection type " + kind);
+                collectionType = null;
+                literalArgs = null;
+        }
+
+        createTable("CREATE TABLE %s (" +
+                    " key int PRIMARY KEY," +
+                    " val " + collectionType + ")");
+
+        ResultMessage.Prepared prepared = QueryProcessor.prepare(
+                                                                String.format("INSERT INTO %s.%s (key, val) VALUES (?, %s)",
+                                                                             KEYSPACE,
+                                                                             currentTable(),
+                                                                             literalArgs),
+                                                                ClientState.forInternalCalls(), false);
+        Assert.assertNotNull(QueryProcessor.instance.getPrepared(prepared.statementId));
+        return prepared;
+    }
+
+    private ResultMessage.Prepared prepareStatementWithDelayedValueTuple(String function)
+    {
+        createTable("CREATE TABLE %s (" +
+                    " key int PRIMARY KEY," +
+                    " val tuple<double> )");
+
+        ResultMessage.Prepared prepared = QueryProcessor.prepare(
+                                                                String.format("INSERT INTO %s.%s (key, val) VALUES (?, (%s(0.0)))",
+                                                                             KEYSPACE,
+                                                                             currentTable(),
+                                                                             function),
+                                                                ClientState.forInternalCalls(), false);
+        Assert.assertNotNull(QueryProcessor.instance.getPrepared(prepared.statementId));
+        return prepared;
+    }
+
+    public void checkDelayedValuesCorrectlyIdentifyFunctionsInUse(boolean dropKeyspace) throws Throwable
+    {
+        // prepare a statement which doesn't use any function for a control
+        createTable("CREATE TABLE %s (" +
+                    " key int PRIMARY KEY," +
+                    " val double)");
+        ResultMessage.Prepared control = QueryProcessor.prepare(
+                                                               String.format("INSERT INTO %s.%s (key, val) VALUES (?, ?)",
+                                                                            KEYSPACE,
+                                                                            currentTable()),
+                                                               ClientState.forInternalCalls(), false);
+        Assert.assertNotNull(QueryProcessor.instance.getPrepared(control.statementId));
+
+        // a function that we'll drop and verify that statements which use it to
+        // provide a DelayedValue are removed from the cache in QueryProcessor
+        String function = createFunction(KEYSPACE_PER_TEST, "double",
+                                        "CREATE FUNCTION %s ( input double ) " +
+                                        "RETURNS double " +
+                                        "LANGUAGE javascript " +
+                                        "AS 'input'");
+        Assert.assertEquals(1, Functions.find(parseFunctionName(function)).size());
+
+        List<ResultMessage.Prepared> prepared = new ArrayList<>();
+        // prepare statements which use the function to provide a DelayedValue
+        prepared.add(prepareStatementWithDelayedValue(CollectionType.Kind.LIST, function));
+        prepared.add(prepareStatementWithDelayedValue(CollectionType.Kind.SET, function));
+        prepared.add(prepareStatementWithDelayedValue(CollectionType.Kind.MAP, function));
+        prepared.add(prepareStatementWithDelayedValueTuple(function));
+
+        // what to drop - the function is scoped to the per-test keyspace, but the prepared statements
+        // select from the per-fixture keyspace. So if we drop the per-test keyspace, the function
+        // should be removed along with the statements that reference it. The control statement should
+        // remain present in the cache. Likewise, if we actually drop the function itself the control
+        // statement should not be removed, but the others should be
+        if (dropKeyspace)
+            dropPerTestKeyspace();
+        else
+            execute("DROP FUNCTION " + function);
+
+        Assert.assertNotNull(QueryProcessor.instance.getPrepared(control.statementId));
+        for (ResultMessage.Prepared removed : prepared)
+            Assert.assertNull(QueryProcessor.instance.getPrepared(removed.statementId));
+    }
+
+    @Test
     public void testFunctionCreationAndDrop() throws Throwable
     {
         createTable("CREATE TABLE %s (key int PRIMARY KEY, d double)");