Repository: cassandra
Updated Branches:
  refs/heads/trunk 75a30c5c2 -> 44fa12ec4


http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java 
b/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java
index be20102..9b7bbf0 100644
--- a/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java
+++ b/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java
@@ -29,7 +29,7 @@ import org.apache.cassandra.utils.UUIDGen;
 
 public abstract class TimeuuidFcts
 {
-    public static final Function nowFct = new AbstractFunction("now", 
TimeUUIDType.instance)
+    public static final Function nowFct = new NativeFunction("now", 
TimeUUIDType.instance)
     {
         public ByteBuffer execute(List<ByteBuffer> parameters)
         {
@@ -43,7 +43,7 @@ public abstract class TimeuuidFcts
         }
     };
 
-    public static final Function minTimeuuidFct = new 
AbstractFunction("mintimeuuid", TimeUUIDType.instance, TimestampType.instance)
+    public static final Function minTimeuuidFct = new 
NativeFunction("mintimeuuid", TimeUUIDType.instance, TimestampType.instance)
     {
         public ByteBuffer execute(List<ByteBuffer> parameters)
         {
@@ -55,7 +55,7 @@ public abstract class TimeuuidFcts
         }
     };
 
-    public static final Function maxTimeuuidFct = new 
AbstractFunction("maxtimeuuid", TimeUUIDType.instance, TimestampType.instance)
+    public static final Function maxTimeuuidFct = new 
NativeFunction("maxtimeuuid", TimeUUIDType.instance, TimestampType.instance)
     {
         public ByteBuffer execute(List<ByteBuffer> parameters)
         {
@@ -67,7 +67,7 @@ public abstract class TimeuuidFcts
         }
     };
 
-    public static final Function dateOfFct = new AbstractFunction("dateof", 
TimestampType.instance, TimeUUIDType.instance)
+    public static final Function dateOfFct = new NativeFunction("dateof", 
TimestampType.instance, TimeUUIDType.instance)
     {
         public ByteBuffer execute(List<ByteBuffer> parameters)
         {
@@ -79,7 +79,7 @@ public abstract class TimeuuidFcts
         }
     };
 
-    public static final Function unixTimestampOfFct = new 
AbstractFunction("unixtimestampof", LongType.instance, TimeUUIDType.instance)
+    public static final Function unixTimestampOfFct = new 
NativeFunction("unixtimestampof", LongType.instance, TimeUUIDType.instance)
     {
         public ByteBuffer execute(List<ByteBuffer> parameters)
         {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/src/java/org/apache/cassandra/cql3/functions/TokenFct.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/TokenFct.java 
b/src/java/org/apache/cassandra/cql3/functions/TokenFct.java
index 5093a72..2504a66 100644
--- a/src/java/org/apache/cassandra/cql3/functions/TokenFct.java
+++ b/src/java/org/apache/cassandra/cql3/functions/TokenFct.java
@@ -22,26 +22,17 @@ import java.util.List;
 
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.ColumnDefinition;
-import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.db.composites.CBuilder;
 import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.dht.IPartitioner;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.service.StorageService;
 
-public class TokenFct extends AbstractFunction
+public class TokenFct extends NativeFunction
 {
     // The actual token function depends on the partitioner used
     private static final IPartitioner partitioner = 
StorageService.getPartitioner();
 
-    public static final Function.Factory factory = new Function.Factory()
-    {
-        public Function create(String ksName, String cfName)
-        {
-            return new TokenFct(Schema.instance.getCFMetaData(ksName, cfName));
-        }
-    };
-
     private final CFMetaData cfm;
 
     public TokenFct(CFMetaData cfm)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/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
new file mode 100644
index 0000000..558fb72
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
@@ -0,0 +1,250 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.*;
+
+import com.google.common.base.Objects;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.CompositeType;
+import org.apache.cassandra.db.marshal.UTF8Type;
+import org.apache.cassandra.db.marshal.TypeParser;
+import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.utils.FBUtilities;
+
+/**
+ * Base class for User Defined Functions.
+ */
+public abstract class UDFunction extends AbstractFunction
+{
+    protected static final Logger logger = 
LoggerFactory.getLogger(UDFunction.class);
+
+    protected final List<ColumnIdentifier> argNames;
+
+    protected final String language;
+    protected final String body;
+    private final boolean deterministic;
+
+    protected UDFunction(FunctionName name,
+                         List<ColumnIdentifier> argNames,
+                         List<AbstractType<?>> argTypes,
+                         AbstractType<?> returnType,
+                         String language,
+                         String body,
+                         boolean deterministic)
+    {
+        super(name, argTypes, returnType);
+        this.argNames = argNames;
+        this.language = language;
+        this.body = body;
+        this.deterministic = deterministic;
+    }
+
+    public static UDFunction create(FunctionName name,
+                                    List<ColumnIdentifier> argNames,
+                                    List<AbstractType<?>> argTypes,
+                                    AbstractType<?> returnType,
+                                    String language,
+                                    String body,
+                                    boolean deterministic)
+    throws InvalidRequestException
+    {
+        switch (language)
+        {
+            case "class": return new ReflectionBasedUDF(name, argNames, 
argTypes, returnType, language, body, deterministic);
+            default: throw new InvalidRequestException(String.format("Invalid 
language %s for '%s'", language, name));
+        }
+    }
+
+    /**
+     * It can happen that a function has been declared (is listed in the 
scheam) but cannot
+     * be loaded (maybe only on some nodes). This is the case for instance if 
the class defining
+     * the class is not on the classpath for some of the node, or after a 
restart. In that case,
+     * we create a "fake" function so that:
+     *  1) the broken function can be dropped easily if that is what people 
want to do.
+     *  2) we return a meaningful error message if the function is executed 
(something more precise
+     *     than saying that the function doesn't exist)
+     */
+    private static UDFunction createBrokenFunction(FunctionName name,
+                                                  List<ColumnIdentifier> 
argNames,
+                                                  List<AbstractType<?>> 
argTypes,
+                                                  AbstractType<?> returnType,
+                                                  String language,
+                                                  String body,
+                                                  final 
InvalidRequestException reason)
+    {
+        return new UDFunction(name, argNames, argTypes, returnType, language, 
body, true)
+        {
+            public ByteBuffer execute(List<ByteBuffer> parameters) throws 
InvalidRequestException
+            {
+                throw new InvalidRequestException(String.format("Function '%s' 
exists but hasn't been loaded successfully for the following reason: %s. "
+                                                              + "Please see 
the server log for more details", this, reason.getMessage()));
+            }
+        };
+    }
+
+    // We allow method overloads, so a function is not uniquely identified by 
its name only, but
+    // also by its argument types. To distinguish overloads of given function 
name in the schema 
+    // we use a "signature" which is just a SHA-1 of it's argument types (we 
could replace that by
+    // using a "signature" UDT that would be comprised of the function name 
and argument types,
+    // which we could then use as clustering column. But as we haven't yet 
used UDT in system tables,
+    // We'll left that decision to #6717).
+    private static ByteBuffer computeSignature(List<AbstractType<?>> argTypes)
+    {
+        MessageDigest digest = FBUtilities.newMessageDigest("SHA-1");
+        for (AbstractType<?> type : argTypes)
+            digest.update(type.toString().getBytes(StandardCharsets.UTF_8));
+        return ByteBuffer.wrap(digest.digest());
+    }
+
+    public boolean isPure()
+    {
+        return deterministic;
+    }
+
+    public boolean isNative()
+    {
+        return false;
+    }
+
+    private static Mutation makeSchemaMutation(FunctionName name)
+    {
+        CompositeType kv = 
(CompositeType)CFMetaData.SchemaFunctionsCf.getKeyValidator();
+        return new Mutation(Keyspace.SYSTEM_KS, kv.decompose(name.namespace, 
name.name));
+    }
+
+    // TODO: we should allow removing just one function, not all functions 
having a given name
+    public static Mutation dropFromSchema(long timestamp, FunctionName fun)
+    {
+        Mutation mutation = makeSchemaMutation(fun);
+        mutation.delete(SystemKeyspace.SCHEMA_FUNCTIONS_CF, timestamp);
+        return mutation;
+    }
+
+    public Mutation toSchemaUpdate(long timestamp)
+    {
+        Mutation mutation = makeSchemaMutation(name);
+        ColumnFamily cf = 
mutation.addOrGet(SystemKeyspace.SCHEMA_FUNCTIONS_CF);
+
+        Composite prefix = 
CFMetaData.SchemaFunctionsCf.comparator.make(computeSignature(argTypes));
+        CFRowAdder adder = new CFRowAdder(cf, prefix, timestamp);
+
+        adder.resetCollection("argument_names");
+        adder.resetCollection("argument_types");
+        adder.add("return_type", returnType.toString());
+        adder.add("language", language);
+        adder.add("body", body);
+        adder.add("deterministic", deterministic);
+
+        for (int i = 0; i < argNames.size(); i++)
+        {
+            adder.addListEntry("argument_names", argNames.get(i).bytes);
+            adder.addListEntry("argument_types", argTypes.get(i).toString());
+        }
+
+        return mutation;
+    }
+
+    public static UDFunction fromSchema(UntypedResultSet.Row row)
+    {
+        String namespace = row.getString("namespace");
+        String fname = row.getString("name");
+        FunctionName name = new FunctionName(namespace, fname);
+
+        List<String> names = row.getList("argument_names", UTF8Type.instance);
+        List<String> types = row.getList("argument_types", UTF8Type.instance);
+
+        List<ColumnIdentifier> argNames = new ArrayList<>(names.size());
+        for (String arg : names)
+            argNames.add(new ColumnIdentifier(arg, true));
+
+        List<AbstractType<?>> argTypes = new ArrayList<>(types.size());
+        for (String type : types)
+            argTypes.add(parseType(type));
+
+        AbstractType<?> returnType = parseType(row.getString("return_type"));
+
+        boolean deterministic = row.getBoolean("deterministic");
+        String language = row.getString("language");
+        String body = row.getString("body");
+
+        try
+        {
+            return create(name, argNames, argTypes, returnType, language, 
body, deterministic);
+        }
+        catch (InvalidRequestException e)
+        {
+            logger.error(String.format("Cannot load function '%s' from schema: 
this function won't be available (on this node)", name), e);
+            return createBrokenFunction(name, argNames, argTypes, returnType, 
language, body, e);
+        }
+    }
+
+    private static AbstractType<?> parseType(String str)
+    {
+        // We only use this when reading the schema where we shouldn't get an 
error
+        try
+        {
+            return TypeParser.parse(str);
+        }
+        catch (SyntaxException | ConfigurationException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static Map<ByteBuffer, UDFunction> fromSchema(Row row)
+    {
+        UntypedResultSet results = QueryProcessor.resultify("SELECT * FROM 
system." + SystemKeyspace.SCHEMA_FUNCTIONS_CF, row);
+        Map<ByteBuffer, UDFunction> udfs = new HashMap<>(results.size());
+        for (UntypedResultSet.Row result : results)
+            udfs.put(result.getBlob("signature"), fromSchema(result));
+        return udfs;
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (!(o instanceof UDFunction))
+            return false;
+
+        UDFunction that = (UDFunction)o;
+        return Objects.equal(this.name, that.name)
+            && Objects.equal(this.argNames, that.argNames)
+            && Objects.equal(this.argTypes, that.argTypes)
+            && Objects.equal(this.returnType, that.returnType)
+            && Objects.equal(this.language, that.language)
+            && Objects.equal(this.body, that.body)
+            && Objects.equal(this.deterministic, that.deterministic);
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return Objects.hashCode(name, argNames, argTypes, returnType, 
language, body, deterministic);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/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 718bcbc..1bf4c17 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UuidFcts.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UuidFcts.java
@@ -26,7 +26,7 @@ import org.apache.cassandra.serializers.UUIDSerializer;
 
 public abstract class UuidFcts
 {
-    public static final Function uuidFct = new AbstractFunction("uuid", 
UUIDType.instance)
+    public static final Function uuidFct = new NativeFunction("uuid", 
UUIDType.instance)
     {
         public ByteBuffer execute(List<ByteBuffer> parameters)
         {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/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 47bacd2..a54409e 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateFunctionStatement.java
@@ -21,57 +21,50 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.cassandra.auth.Permission;
-import org.apache.cassandra.config.Schema;
-import org.apache.cassandra.config.UFMetaData;
 import org.apache.cassandra.cql3.CQL3Type;
 import org.apache.cassandra.cql3.ColumnIdentifier;
-import org.apache.cassandra.cql3.QueryOptions;
-import org.apache.cassandra.cql3.functions.Functions;
-import org.apache.cassandra.cql3.udf.UDFRegistry;
+import org.apache.cassandra.cql3.functions.*;
+import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.exceptions.RequestValidationException;
 import org.apache.cassandra.exceptions.UnauthorizedException;
 import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.MigrationManager;
-import org.apache.cassandra.service.QueryState;
 import org.apache.cassandra.transport.Event;
-import org.apache.cassandra.transport.messages.ResultMessage;
 
 /**
  * A <code>CREATE FUNCTION</code> statement parsed from a CQL query.
  */
 public final class CreateFunctionStatement extends SchemaAlteringStatement
 {
-    final boolean orReplace;
-    final boolean ifNotExists;
-    final String namespace;
-    final String functionName;
-    final String qualifiedName;
-    final String language;
-    final String body;
-    final boolean deterministic;
-    final CQL3Type.Raw returnType;
-    final List<Argument> arguments;
-
-    private UFMetaData ufMeta;
-
-    public CreateFunctionStatement(String namespace, String functionName, 
String language, String body, boolean deterministic,
-                                   CQL3Type.Raw returnType, List<Argument> 
arguments, boolean orReplace, boolean ifNotExists)
+    private final boolean orReplace;
+    private final boolean ifNotExists;
+    private final FunctionName functionName;
+    private final String language;
+    private final String body;
+    private final boolean deterministic;
+
+    private final List<ColumnIdentifier> argNames;
+    private final List<CQL3Type.Raw> argRawTypes;
+    private final CQL3Type.Raw rawReturnType;
+
+    public CreateFunctionStatement(FunctionName functionName,
+                                   String language,
+                                   String body,
+                                   boolean deterministic,
+                                   List<ColumnIdentifier> argNames,
+                                   List<CQL3Type.Raw> argRawTypes,
+                                   CQL3Type.Raw rawReturnType,
+                                   boolean orReplace,
+                                   boolean ifNotExists)
     {
-        super();
-        this.namespace = namespace != null ? namespace : "";
         this.functionName = functionName;
-        this.qualifiedName = UFMetaData.qualifiedName(namespace, functionName);
         this.language = language;
         this.body = body;
         this.deterministic = deterministic;
-        this.returnType = returnType;
-        this.arguments = arguments;
-        assert functionName != null : "null function name";
-        assert language != null : "null function language";
-        assert body != null : "null function body";
-        assert returnType != null : "null function returnType";
-        assert arguments != null : "null function arguments";
+        this.argNames = argNames;
+        this.argRawTypes = argRawTypes;
+        this.rawReturnType = rawReturnType;
         this.orReplace = orReplace;
         this.ifNotExists = ifNotExists;
     }
@@ -83,23 +76,10 @@ public final class CreateFunctionStatement extends 
SchemaAlteringStatement
         state.hasAllKeyspacesAccess(Permission.CREATE);
     }
 
-    /**
-     * The <code>CqlParser</code> only goes as far as extracting the keyword 
arguments
-     * from these statements, so this method is responsible for processing and
-     * validating.
-     *
-     * @throws org.apache.cassandra.exceptions.InvalidRequestException if 
arguments are missing or unacceptable
-     */
-    public void validate(ClientState state) throws RequestValidationException
+    public void validate(ClientState state) throws InvalidRequestException
     {
-        if (!namespace.isEmpty() && !namespace.matches("\\w+"))
-            throw new InvalidRequestException(String.format("\"%s\" is not a 
valid function name", qualifiedName));
-        if (!functionName.matches("\\w+"))
-            throw new InvalidRequestException(String.format("\"%s\" is not a 
valid function name", qualifiedName));
-        if (namespace.length() > Schema.NAME_LENGTH)
-            throw new InvalidRequestException(String.format("UDF namespace 
names shouldn't be more than %s characters long (got \"%s\")", 
Schema.NAME_LENGTH, qualifiedName));
-        if (functionName.length() > Schema.NAME_LENGTH)
-            throw new InvalidRequestException(String.format("UDF function 
names shouldn't be more than %s characters long (got \"%s\")", 
Schema.NAME_LENGTH, qualifiedName));
+        if (ifNotExists && orReplace)
+            throw new InvalidRequestException("Cannot use both 'OR REPLACE' 
and 'IF NOT EXISTS' directives");
     }
 
     public Event.SchemaChange changeEvent()
@@ -107,75 +87,34 @@ public final class CreateFunctionStatement extends 
SchemaAlteringStatement
         return null;
     }
 
-    public ResultMessage executeInternal(QueryState state, QueryOptions 
options)
-    {
-        try
-        {
-            doExecute();
-            return super.executeInternal(state, options);
-        }
-        catch (RequestValidationException e)
-        {
-            throw new RuntimeException(e);
-        }
-    }
-
-    public ResultMessage execute(QueryState state, QueryOptions options) 
throws RequestValidationException
-    {
-        doExecute();
-        return super.execute(state, options);
-    }
-
-    private void doExecute() throws RequestValidationException
-    {
-        boolean exists = UDFRegistry.hasFunction(qualifiedName);
-        if (exists && ifNotExists)
-            throw new InvalidRequestException(String.format("Function '%s' 
already exists.", qualifiedName));
-        if (exists && !orReplace)
-            throw new InvalidRequestException(String.format("Function '%s' 
already exists.", qualifiedName));
-
-        if (namespace.isEmpty() && Functions.contains(functionName))
-            throw new InvalidRequestException(String.format("Function name 
'%s' is reserved by CQL.", qualifiedName));
-
-        List<Argument> args = arguments;
-        List<String> argumentNames = new ArrayList<>(args.size());
-        List<String> argumentTypes = new ArrayList<>(args.size());
-        for (Argument arg : args)
-        {
-            argumentNames.add(arg.getName().toString());
-            argumentTypes.add(arg.getType().toString());
-        }
-        this.ufMeta = new UFMetaData(namespace, functionName, deterministic, 
argumentNames, argumentTypes,
-                                     returnType.toString(), language, body);
-
-        UDFRegistry.tryCreateFunction(ufMeta);
-    }
-
     public boolean announceMigration(boolean isLocalOnly) throws 
RequestValidationException
     {
-        MigrationManager.announceNewFunction(ufMeta, isLocalOnly);
-        return true;
-    }
-
-    public static final class Argument
-    {
-        final ColumnIdentifier name;
-        final CQL3Type.Raw type;
+        List<AbstractType<?>> argTypes = new ArrayList<>(argRawTypes.size());
+        for (CQL3Type.Raw rawType : argRawTypes)
+            // We have no proper keyspace to give, which means that this will 
break (NPE currently)
+            // for UDT: #7791 is open to fix this
+            argTypes.add(rawType.prepare(null).getType());
 
-        public Argument(ColumnIdentifier name, CQL3Type.Raw type)
-        {
-            this.name = name;
-            this.type = type;
-        }
+        AbstractType<?> returnType = rawReturnType.prepare(null).getType();
 
-        public ColumnIdentifier getName()
+        Function old = Functions.find(functionName, argTypes);
+        if (old != null)
         {
-            return name;
+            if (ifNotExists)
+                return false;
+            if (!orReplace)
+                throw new InvalidRequestException(String.format("Function %s 
already exists", old));
+
+            // Means we're replacing the function. We still need to validate 
that 1) it's not a native function and 2) that the return type
+            // matches (or that could break existing code badly)
+            if (old.isNative())
+                throw new InvalidRequestException(String.format("Cannot 
replace native function %s", old));
+            if (!old.returnType().isValueCompatibleWith(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()));
         }
 
-        public CQL3Type.Raw getType()
-        {
-            return type;
-        }
+        MigrationManager.announceNewFunction(UDFunction.create(functionName, 
argNames, argTypes, returnType, language, body, deterministic), isLocalOnly);
+        return true;
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/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 159f385..4c963a8 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropFunctionStatement.java
@@ -17,9 +17,10 @@
  */
 package org.apache.cassandra.cql3.statements;
 
+import java.util.List;
+
 import org.apache.cassandra.auth.Permission;
-import org.apache.cassandra.config.Schema;
-import org.apache.cassandra.config.UFMetaData;
+import org.apache.cassandra.cql3.functions.*;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.exceptions.RequestValidationException;
 import org.apache.cassandra.exceptions.UnauthorizedException;
@@ -32,17 +33,12 @@ import org.apache.cassandra.transport.Event;
  */
 public final class DropFunctionStatement extends SchemaAlteringStatement
 {
-    private final String namespace;
-    private final String functionName;
-    private final String qualifiedName;
+    private final FunctionName functionName;
     private final boolean ifExists;
 
-    public DropFunctionStatement(String namespace, String functionName, 
boolean ifExists)
+    public DropFunctionStatement(FunctionName functionName, boolean ifExists)
     {
-        super();
-        this.namespace = namespace == null ? "" : namespace;
         this.functionName = functionName;
-        this.qualifiedName = UFMetaData.qualifiedName(namespace, functionName);
         this.ifExists = ifExists;
     }
 
@@ -62,14 +58,6 @@ public final class DropFunctionStatement extends 
SchemaAlteringStatement
      */
     public void validate(ClientState state) throws RequestValidationException
     {
-        if (!namespace.isEmpty() && !namespace.matches("\\w+"))
-            throw new InvalidRequestException(String.format("\"%s\" is not a 
valid function name", qualifiedName));
-        if (!functionName.matches("\\w+"))
-            throw new InvalidRequestException(String.format("\"%s\" is not a 
valid function name", qualifiedName));
-        if (namespace.length() > Schema.NAME_LENGTH)
-            throw new InvalidRequestException(String.format("UDF namespace 
names shouldn't be more than %s characters long (got \"%s\")", 
Schema.NAME_LENGTH, qualifiedName));
-        if (functionName.length() > Schema.NAME_LENGTH)
-            throw new InvalidRequestException(String.format("UDF function 
names shouldn't be more than %s characters long (got \"%s\")", 
Schema.NAME_LENGTH, qualifiedName));
     }
 
     public Event.SchemaChange changeEvent()
@@ -77,20 +65,21 @@ public final class DropFunctionStatement extends 
SchemaAlteringStatement
         return null;
     }
 
-    // no execute() - drop propagated via MigrationManager
-
     public boolean announceMigration(boolean isLocalOnly) throws 
RequestValidationException
     {
-        try
-        {
-            MigrationManager.announceFunctionDrop(namespace, functionName, 
isLocalOnly);
-            return true;
-        }
-        catch (InvalidRequestException e)
+        List<Function> olds = Functions.find(functionName);
+        if (olds == null || olds.isEmpty())
         {
             if (ifExists)
                 return false;
-            throw e;
+            throw new InvalidRequestException(String.format("Cannot drop non 
existing function '%s'", functionName));
         }
+
+        for (Function f : olds)
+            if (f.isNative())
+                throw new InvalidRequestException(String.format("Cannot drop 
function '%s' because it has native overloads", functionName));
+
+        MigrationManager.announceFunctionDrop(functionName, isLocalOnly);
+        return true;
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/src/java/org/apache/cassandra/cql3/statements/Selectable.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/Selectable.java 
b/src/java/org/apache/cassandra/cql3/statements/Selectable.java
index ab0a5a3..be47d17 100644
--- a/src/java/org/apache/cassandra/cql3/statements/Selectable.java
+++ b/src/java/org/apache/cassandra/cql3/statements/Selectable.java
@@ -21,6 +21,7 @@ package org.apache.cassandra.cql3.statements;
 import java.util.List;
 
 import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.cql3.functions.FunctionName;
 
 public interface Selectable
 {
@@ -44,13 +45,11 @@ public interface Selectable
 
     public static class WithFunction implements Selectable
     {
-        public final String namespace;
-        public final String functionName;
+        public final FunctionName functionName;
         public final List<Selectable> args;
 
-        public WithFunction(String namespace, String functionName, 
List<Selectable> args)
+        public WithFunction(FunctionName functionName, List<Selectable> args)
         {
-            this.namespace = namespace;
             this.functionName = functionName;
             this.args = args;
         }
@@ -59,8 +58,6 @@ public interface Selectable
         public String toString()
         {
             StringBuilder sb = new StringBuilder();
-            if (!namespace.isEmpty())
-                sb.append(namespace).append("::");
             sb.append(functionName).append("(");
             for (int i = 0; i < args.size(); i++)
             {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/src/java/org/apache/cassandra/cql3/statements/Selection.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/Selection.java 
b/src/java/org/apache/cassandra/cql3/statements/Selection.java
index 325ef15..20211b2 100644
--- a/src/java/org/apache/cassandra/cql3/statements/Selection.java
+++ b/src/java/org/apache/cassandra/cql3/statements/Selection.java
@@ -29,8 +29,6 @@ import org.apache.cassandra.cql3.functions.Function;
 import org.apache.cassandra.cql3.functions.Functions;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.ColumnDefinition;
-import org.apache.cassandra.cql3.udf.UDFunction;
-import org.apache.cassandra.cql3.udf.UDFRegistry;
 import org.apache.cassandra.db.Cell;
 import org.apache.cassandra.db.CounterCell;
 import org.apache.cassandra.db.ExpiringCell;
@@ -163,25 +161,11 @@ public abstract class Selection
                 args.add(makeSelector(cfm, new RawSelector(rawArg, null), 
defs, null));
 
             // resolve built-in functions before user defined functions
-            AbstractType<?> returnType = 
Functions.getReturnType(withFun.functionName, cfm.ksName, cfm.cfName);
-            if (returnType == null)
-            {
-                UDFunction userFun = 
UDFRegistry.resolveFunction(withFun.namespace, withFun.functionName, 
cfm.ksName, cfm.cfName, args);
-                if (userFun != null)
-                {
-                    // got a user defined function to call
-                    Function fun = userFun.create(args);
-                    ColumnSpecification spec = makeFunctionSpec(cfm, withFun, 
fun.returnType(), raw.alias);
-                    if (metadata != null)
-                        metadata.add(spec);
-                    return new FunctionSelector(userFun.create(args), args);
-                }
-                throw new InvalidRequestException(String.format("Unknown 
function '%s'", withFun.namespace.isEmpty() ? withFun.functionName : 
withFun.namespace + "::" + withFun.functionName));
-            }
-            ColumnSpecification spec = makeFunctionSpec(cfm, withFun, 
returnType, raw.alias);
-            Function fun = Functions.get(cfm.ksName, withFun.functionName, 
args, spec);
+            Function fun = Functions.get(cfm.ksName, withFun.functionName, 
args, cfm.ksName, cfm.cfName);
+            if (fun == null)
+                throw new InvalidRequestException(String.format("Unknown 
function '%s'", withFun.functionName));
             if (metadata != null)
-                metadata.add(spec);
+                metadata.add(makeFunctionSpec(cfm, withFun, fun.returnType(), 
raw.alias));
             return new FunctionSelector(fun, args);
         }
     }
@@ -208,7 +192,7 @@ public abstract class Selection
                                                         ColumnIdentifier 
alias) throws InvalidRequestException
     {
         if (returnType == null)
-            throw new InvalidRequestException(String.format("Unknown function 
%s called in selection clause", fun.namespace.isEmpty() ? fun.functionName : 
fun.namespace +"::"+fun.functionName));
+            throw new InvalidRequestException(String.format("Unknown function 
%s called in selection clause", fun.functionName));
 
         return new ColumnSpecification(cfm.ksName,
                                        cfm.cfName,
@@ -385,14 +369,19 @@ public abstract class Selection
         }
     }
 
-    private static abstract class Selector implements AssignementTestable
+    private static abstract class Selector implements AssignmentTestable
     {
         public abstract ByteBuffer compute(ResultSetBuilder rs) throws 
InvalidRequestException;
         public abstract AbstractType<?> getType();
 
-        public boolean isAssignableTo(String keyspace, ColumnSpecification 
receiver)
+        public AssignmentTestable.TestResult testAssignment(String keyspace, 
ColumnSpecification receiver)
         {
-            return receiver.type.isValueCompatibleWith(getType());
+            if (receiver.type.equals(getType()))
+                return AssignmentTestable.TestResult.EXACT_MATCH;
+            else if (receiver.type.isValueCompatibleWith(getType()))
+                return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE;
+            else
+                return AssignmentTestable.TestResult.NOT_ASSIGNABLE;
         }
     }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/src/java/org/apache/cassandra/cql3/udf/UDFFunctionOverloads.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/udf/UDFFunctionOverloads.java 
b/src/java/org/apache/cassandra/cql3/udf/UDFFunctionOverloads.java
deleted file mode 100644
index aa6892a..0000000
--- a/src/java/org/apache/cassandra/cql3/udf/UDFFunctionOverloads.java
+++ /dev/null
@@ -1,87 +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.udf;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.cassandra.config.UFMetaData;
-import org.apache.cassandra.cql3.AssignementTestable;
-import org.apache.cassandra.exceptions.InvalidRequestException;
-
-public final class UDFFunctionOverloads
-{
-    final Map<String, UFMetaData> signatureMap = new ConcurrentHashMap<>();
-    final Map<String, UDFunction> udfInstances = new ConcurrentHashMap<>();
-
-    public void addAndInit(UFMetaData uf, boolean addIfInvalid)
-    {
-        try
-        {
-            UDFunction UDFunction = new UDFunction(uf);
-            udfInstances.put(uf.signature, UDFunction);
-        }
-        catch (InvalidRequestException e)
-        {
-            uf.invalid = e;
-        }
-
-        if (uf.invalid == null || addIfInvalid)
-            signatureMap.put(uf.signature, uf);
-    }
-
-    public void remove(UFMetaData uf)
-    {
-        signatureMap.remove(uf.signature);
-        udfInstances.remove(uf.signature);
-    }
-
-    public Collection<UFMetaData> values()
-    {
-        return signatureMap.values();
-    }
-
-    public boolean isEmpty()
-    {
-        return signatureMap.isEmpty();
-    }
-
-    public UDFunction resolveFunction(String ksName, String cfName, List<? 
extends AssignementTestable> args)
-    throws InvalidRequestException
-    {
-        for (UFMetaData candidate : signatureMap.values())
-        {
-            // Currently the UDF implementation must use concrete types (like 
Double, Integer) instead of base types (like Number).
-            // To support handling of base types it is necessary to construct 
new, temporary instances of UDFFunction with the
-            // signature for the current request in UDFFunction#argsType + 
UDFFunction#returnType.
-            // Additionally we need the requested return type 
(AssignementTestable) has a parameter for this method.
-            if (candidate.compatibleArgs(ksName, cfName, args))
-            {
-
-                // TODO CASSANDRA-7557 (specific per-function EXECUTE 
permission ??)
-
-                if (candidate.invalid != null)
-                    throw new 
InvalidRequestException(candidate.invalid.getMessage());
-                return udfInstances.get(candidate.signature);
-            }
-        }
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/src/java/org/apache/cassandra/cql3/udf/UDFRegistry.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/udf/UDFRegistry.java 
b/src/java/org/apache/cassandra/cql3/udf/UDFRegistry.java
deleted file mode 100644
index cb3f1a1..0000000
--- a/src/java/org/apache/cassandra/cql3/udf/UDFRegistry.java
+++ /dev/null
@@ -1,146 +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.udf;
-
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.cassandra.config.UFMetaData;
-import org.apache.cassandra.cql3.AssignementTestable;
-import org.apache.cassandra.cql3.QueryProcessor;
-import org.apache.cassandra.cql3.UntypedResultSet;
-import org.apache.cassandra.cql3.functions.Functions;
-import org.apache.cassandra.db.Keyspace;
-import org.apache.cassandra.db.SystemKeyspace;
-import org.apache.cassandra.exceptions.InvalidRequestException;
-
-/**
- * Central registry for user defined functions (CASSANDRA-7395).
- * <p/>
- * UDFs are maintained in {@code system.schema_functions} table and 
distributed to all nodes.
- * <p/>
- * UDFs are not maintained in {@link 
org.apache.cassandra.cql3.functions.Functions} class to have a strict
- * distinction between 'core CQL' functions provided by Cassandra and 
functions provided by the user.
- * 'Core CQL' functions have precedence over UDFs.
- */
-public class UDFRegistry
-{
-    private static final Logger logger = 
LoggerFactory.getLogger(UDFRegistry.class);
-
-    static final String SELECT_CQL = "SELECT namespace, name, signature, 
deterministic, argument_names, argument_types, " +
-                                     "return_type, language, body FROM " +
-                                     Keyspace.SYSTEM_KS + '.' + 
SystemKeyspace.SCHEMA_FUNCTIONS_CF;
-
-    private static final Map<String, UDFFunctionOverloads> functions = new 
ConcurrentHashMap<>();
-
-    public static void init()
-    {
-        refreshInitial();
-    }
-
-    /**
-     * Initial loading of all existing UDFs.
-     */
-    public static void refreshInitial()
-    {
-        logger.debug("Refreshing UDFs");
-        for (UntypedResultSet.Row row : 
QueryProcessor.executeOnceInternal(SELECT_CQL))
-        {
-            UFMetaData uf = UFMetaData.fromSchema(row);
-            UDFFunctionOverloads sigMap = functions.get(uf.qualifiedName);
-            if (sigMap == null)
-                functions.put(uf.qualifiedName, sigMap = new 
UDFFunctionOverloads());
-
-            if (Functions.contains(uf.qualifiedName))
-                logger.warn("The UDF '" + uf.functionName + "' cannot be used 
because it uses the same name as the CQL " +
-                            "function with the same name. You should drop this 
function but can do a " +
-                            "'DESCRIBE FUNCTION "+uf.functionName+";' in cqlsh 
before to get more information about it.");
-
-            // add the function to the registry even if it is invalid (to be 
able to drop it)
-            sigMap.addAndInit(uf, true);
-
-            if (uf.invalid != null)
-                logger.error("Loaded invalid UDF : " + 
uf.invalid.getMessage());
-        }
-    }
-
-    public static boolean hasFunction(String qualifiedName)
-    {
-        UDFFunctionOverloads sigMap = 
functions.get(qualifiedName.toLowerCase());
-        return sigMap != null && !sigMap.isEmpty();
-    }
-
-    public static UDFunction resolveFunction(String namespace, String 
functionName, String ksName, String cfName,
-                                             List<? extends 
AssignementTestable> args)
-    throws InvalidRequestException
-    {
-        UDFFunctionOverloads sigMap = 
functions.get(UFMetaData.qualifiedName(namespace, functionName));
-        if (sigMap != null)
-            return sigMap.resolveFunction(ksName, cfName, args);
-        return null;
-    }
-
-    public static void migrateDropFunction(UFMetaData uf)
-    {
-        UDFFunctionOverloads sigMap = functions.get(uf.qualifiedName);
-        if (sigMap == null)
-            return;
-
-        sigMap.remove(uf);
-    }
-
-    public static void migrateUpdateFunction(UFMetaData uf)
-    {
-        migrateAddFunction(uf);
-    }
-
-    public static void migrateAddFunction(UFMetaData uf)
-    {
-        addFunction(uf, true);
-    }
-
-    /**
-     * Used by {@link 
org.apache.cassandra.cql3.statements.CreateFunctionStatement} to create or 
replace a new function.
-     */
-    public static void tryCreateFunction(UFMetaData ufMeta) throws 
InvalidRequestException
-    {
-        addFunction(ufMeta, false);
-
-        if (ufMeta.invalid != null)
-            throw ufMeta.invalid;
-    }
-
-    private static void addFunction(UFMetaData uf, boolean addIfInvalid)
-    {
-        UDFFunctionOverloads sigMap = functions.get(uf.qualifiedName);
-        if (sigMap == null)
-            functions.put(uf.qualifiedName, sigMap = new 
UDFFunctionOverloads());
-
-        sigMap.addAndInit(uf, addIfInvalid);
-    }
-
-    public static UDFFunctionOverloads getFunctionSigMap(String qualifiedName)
-    {
-        return functions.get(qualifiedName);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/src/java/org/apache/cassandra/cql3/udf/UDFunction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/udf/UDFunction.java 
b/src/java/org/apache/cassandra/cql3/udf/UDFunction.java
deleted file mode 100644
index 61e52e5..0000000
--- a/src/java/org/apache/cassandra/cql3/udf/UDFunction.java
+++ /dev/null
@@ -1,182 +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.udf;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.cassandra.config.UFMetaData;
-import org.apache.cassandra.cql3.AssignementTestable;
-import org.apache.cassandra.cql3.functions.Function;
-import org.apache.cassandra.db.marshal.AbstractType;
-import org.apache.cassandra.exceptions.InvalidRequestException;
-
-/**
- * UDFunction contains the <i>invokable</i> instance of a user defined 
function.
- * Currently (as of CASSANDRA-7395) only {@code public static} methods in a 
{@link public} class
- * can be invoked.
- * CASSANDRA-7562 will introduce Java source code UDFs and CASSANDRA-7526 will 
introduce JSR-223 scripting languages.
- * Invocations of UDFs are routed via this class.
- */
-public class UDFunction
-{
-    private static final Logger logger = 
LoggerFactory.getLogger(UDFunction.class);
-
-    public final UFMetaData meta;
-
-    public final Method method;
-
-    UDFunction(UFMetaData meta) throws InvalidRequestException
-    {
-        this.meta = meta;
-
-        Method m;
-        switch (meta.language)
-        {
-            case "class":
-                m = resolveClassMethod();
-                break;
-            default:
-                throw new InvalidRequestException("Invalid UDF language " + 
meta.language + " for '" + meta.qualifiedName + '\'');
-        }
-        this.method = m;
-    }
-
-    private Method resolveClassMethod() throws InvalidRequestException
-    {
-        Class<?> jReturnType = 
meta.cqlReturnType.getType().getSerializer().getType();
-        Class<?> paramTypes[] = new Class[meta.cqlArgumentTypes.size()];
-        for (int i = 0; i < paramTypes.length; i++)
-            paramTypes[i] = 
meta.cqlArgumentTypes.get(i).getType().getSerializer().getType();
-
-        String className;
-        String methodName;
-        int i = meta.body.indexOf('#');
-        if (i != -1)
-        {
-            methodName = meta.body.substring(i + 1);
-            className = meta.body.substring(0, i);
-        }
-        else
-        {
-            methodName = meta.functionName;
-            className = meta.body;
-        }
-        try
-        {
-            Class<?> cls = Class.forName(className, false, 
Thread.currentThread().getContextClassLoader());
-
-            Method method = cls.getMethod(methodName, paramTypes);
-
-            if (!Modifier.isStatic(method.getModifiers()))
-                throw new InvalidRequestException("Method " + className + '.' 
+ methodName + '(' + Arrays.toString(paramTypes) + ") is not static");
-
-            if (!jReturnType.isAssignableFrom(method.getReturnType()))
-            {
-                throw new InvalidRequestException("Method " + className + '.' 
+ methodName + '(' + Arrays.toString(paramTypes) + ") " +
-                                                  "has incompatible return 
type " + method.getReturnType() + " (not assignable to " + jReturnType + ')');
-            }
-
-            return method;
-        }
-        catch (ClassNotFoundException e)
-        {
-            throw new InvalidRequestException("Class " + className + " does 
not exist");
-        }
-        catch (NoSuchMethodException e)
-        {
-            throw new InvalidRequestException("Method " + className + '.' + 
methodName + '(' + Arrays.toString(paramTypes) + ") does not exist");
-        }
-    }
-
-    public Function create(List<? extends AssignementTestable> providedArgs)
-    {
-        final int argCount = providedArgs.size();
-        final List<AbstractType<?>> argsType = new ArrayList<>(argCount);
-        final AbstractType<?> returnType = meta.cqlReturnType.getType();
-        for (int i = 0; i < argCount; i++)
-        {
-            AbstractType<?> argType = meta.cqlArgumentTypes.get(i).getType();
-            argsType.add(argType);
-        }
-
-        return new Function()
-        {
-            public String name()
-            {
-                return meta.qualifiedName;
-            }
-
-            public List<AbstractType<?>> argsType()
-            {
-                return argsType;
-            }
-
-            public AbstractType<?> returnType()
-            {
-                return returnType;
-            }
-
-            public ByteBuffer execute(List<ByteBuffer> parameters) throws 
InvalidRequestException
-            {
-                Object[] parms = new Object[argCount];
-                for (int i = 0; i < parms.length; i++)
-                {
-                    ByteBuffer bb = parameters.get(i);
-                    if (bb != null)
-                    {
-                        AbstractType<?> argType = argsType.get(i);
-                        parms[i] = argType.compose(bb);
-                    }
-                }
-
-                Object result;
-                try
-                {
-                    result = method.invoke(null, parms);
-                    @SuppressWarnings("unchecked") ByteBuffer r = result != 
null ? ((AbstractType) returnType).decompose(result) : null;
-                    return r;
-                }
-                catch (InvocationTargetException e)
-                {
-                    Throwable c = e.getCause();
-                    logger.error("Invocation of UDF {} failed", 
meta.qualifiedName, c);
-                    throw new InvalidRequestException("Invocation of UDF " + 
meta.qualifiedName + " failed: " + c);
-                }
-                catch (IllegalAccessException e)
-                {
-                    throw new InvalidRequestException("UDF " + 
meta.qualifiedName + " invocation failed: " + e);
-                }
-            }
-
-            public boolean isPure()
-            {
-                return meta.deterministic;
-            }
-        };
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/src/java/org/apache/cassandra/db/DefsTables.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/DefsTables.java 
b/src/java/org/apache/cassandra/db/DefsTables.java
index e8692a7..f1fe0bf 100644
--- a/src/java/org/apache/cassandra/db/DefsTables.java
+++ b/src/java/org/apache/cassandra/db/DefsTables.java
@@ -24,9 +24,6 @@ import java.util.*;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.MapDifference;
 import com.google.common.collect.Maps;
-import org.apache.cassandra.config.UFMetaData;
-import org.apache.cassandra.cql3.udf.UDFRegistry;
-import org.apache.cassandra.db.commitlog.CommitLog;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,6 +32,9 @@ import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.KSMetaData;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.config.UTMetaData;
+import org.apache.cassandra.cql3.functions.Functions;
+import org.apache.cassandra.cql3.functions.UDFunction;
+import org.apache.cassandra.db.commitlog.CommitLog;
 import org.apache.cassandra.db.compaction.CompactionManager;
 import org.apache.cassandra.db.filter.QueryFilter;
 import org.apache.cassandra.db.marshal.AsciiType;
@@ -393,8 +393,8 @@ public class DefsTables
             if (!cfFunctions.hasColumns())
                 continue;
 
-            for (UFMetaData uf : UFMetaData.fromSchema(new Row(entry.getKey(), 
cfFunctions)).values())
-                addFunction(uf);
+            for (UDFunction udf : UDFunction.fromSchema(new 
Row(entry.getKey(), cfFunctions)).values())
+                addFunction(udf);
         }
 
         for (Map.Entry<DecoratedKey, 
MapDifference.ValueDifference<ColumnFamily>> modifiedEntry : 
diff.entriesDiffering().entrySet())
@@ -405,26 +405,26 @@ public class DefsTables
 
             if (!prevCFFunctions.hasColumns()) // whole namespace was deleted 
and now it's re-created
             {
-                for (UFMetaData uf : UFMetaData.fromSchema(new Row(namespace, 
newCFFunctions)).values())
-                    addFunction(uf);
+                for (UDFunction udf : UDFunction.fromSchema(new Row(namespace, 
newCFFunctions)).values())
+                    addFunction(udf);
             }
             else if (!newCFFunctions.hasColumns()) // whole namespace is 
deleted
             {
-                for (UFMetaData uf : UFMetaData.fromSchema(new Row(namespace, 
prevCFFunctions)).values())
-                    dropFunction(uf);
+                for (UDFunction udf : UDFunction.fromSchema(new Row(namespace, 
prevCFFunctions)).values())
+                    dropFunction(udf);
             }
             else // has modifications in the functions, need to perform nested 
diff to determine what was really changed
             {
-                MapDifference<String, UFMetaData> functionsDiff = 
Maps.difference(UFMetaData.fromSchema(new Row(namespace, prevCFFunctions)),
-                    UFMetaData.fromSchema(new Row(namespace, newCFFunctions)));
+                MapDifference<ByteBuffer, UDFunction> functionsDiff = 
Maps.difference(UDFunction.fromSchema(new Row(namespace, prevCFFunctions)),
+                                                                               
       UDFunction.fromSchema(new Row(namespace, newCFFunctions)));
 
-                for (UFMetaData function : 
functionsDiff.entriesOnlyOnRight().values())
-                    addFunction(function);
+                for (UDFunction udf : 
functionsDiff.entriesOnlyOnRight().values())
+                    addFunction(udf);
 
-                for (UFMetaData function : 
functionsDiff.entriesOnlyOnLeft().values())
-                    dropFunction(function);
+                for (UDFunction udf : 
functionsDiff.entriesOnlyOnLeft().values())
+                    dropFunction(udf);
 
-                for (MapDifference.ValueDifference<UFMetaData> tdiff : 
functionsDiff.entriesDiffering().values())
+                for (MapDifference.ValueDifference<UDFunction> tdiff : 
functionsDiff.entriesDiffering().values())
                     updateFunction(tdiff.rightValue()); // use the most recent 
value
             }
         }
@@ -478,14 +478,14 @@ public class DefsTables
             MigrationManager.instance.notifyCreateUserType(ut);
     }
 
-    private static void addFunction(UFMetaData uf)
+    private static void addFunction(UDFunction udf)
     {
-        logger.info("Loading {}", uf);
+        logger.info("Loading {}", udf);
 
-        UDFRegistry.migrateAddFunction(uf);
+        Functions.addFunction(udf);
 
         if (!StorageService.instance.isClientMode())
-            MigrationManager.instance.notifyCreateFunction(uf);
+            MigrationManager.instance.notifyCreateFunction(udf);
     }
 
     private static void updateKeyspace(KSMetaData newState)
@@ -530,14 +530,14 @@ public class DefsTables
             MigrationManager.instance.notifyUpdateUserType(ut);
     }
 
-    private static void updateFunction(UFMetaData uf)
+    private static void updateFunction(UDFunction udf)
     {
-        logger.info("Updating {}", uf);
+        logger.info("Updating {}", udf);
 
-        UDFRegistry.migrateUpdateFunction(uf);
+        Functions.replaceFunction(udf);
 
         if (!StorageService.instance.isClientMode())
-            MigrationManager.instance.notifyUpdateFunction(uf);
+            MigrationManager.instance.notifyUpdateFunction(udf);
     }
 
     private static void dropKeyspace(String ksName)
@@ -619,14 +619,15 @@ public class DefsTables
             MigrationManager.instance.notifyDropUserType(ut);
     }
 
-    private static void dropFunction(UFMetaData uf)
+    private static void dropFunction(UDFunction udf)
     {
-        logger.info("Drop {}", uf);
+        logger.info("Drop {}", udf);
 
-        UDFRegistry.migrateDropFunction(uf);
+        // TODO: this is kind of broken as this remove all overloads of the 
function name
+        Functions.removeFunction(udf.name(), udf.argTypes());
 
         if (!StorageService.instance.isClientMode())
-            MigrationManager.instance.notifyDropFunction(uf);
+            MigrationManager.instance.notifyDropFunction(udf);
     }
 
     private static KSMetaData makeNewKeyspaceDefinition(KSMetaData ksm, 
CFMetaData toExclude)
@@ -644,4 +645,3 @@ public class DefsTables
             SystemKeyspace.forceBlockingFlush(cf);
     }
 }
-

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/src/java/org/apache/cassandra/service/CassandraDaemon.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/CassandraDaemon.java 
b/src/java/org/apache/cassandra/service/CassandraDaemon.java
index daa135f..2153c85 100644
--- a/src/java/org/apache/cassandra/service/CassandraDaemon.java
+++ b/src/java/org/apache/cassandra/service/CassandraDaemon.java
@@ -33,7 +33,6 @@ import javax.management.StandardMBean;
 
 import com.google.common.collect.Iterables;
 import com.google.common.util.concurrent.Uninterruptibles;
-import org.apache.cassandra.cql3.udf.UDFRegistry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,6 +43,7 @@ import org.apache.cassandra.concurrent.StageManager;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.Schema;
+import org.apache.cassandra.cql3.functions.Functions;
 import org.apache.cassandra.db.ColumnFamilyStore;
 import org.apache.cassandra.db.Directories;
 import org.apache.cassandra.db.Keyspace;
@@ -235,8 +235,9 @@ public class CassandraDaemon
             System.exit(100);
         }
 
-        // load keyspace descriptions.
+        // load keyspace && function descriptions.
         DatabaseDescriptor.loadSchemas();
+        Functions.loadUDFFromSchema();
 
         // clean up compaction leftovers
         Map<Pair<String, String>, Map<Integer, UUID>> unfinishedCompactions = 
SystemKeyspace.getUnfinishedCompactions();
@@ -366,9 +367,6 @@ public class CassandraDaemon
         if 
(!FBUtilities.getBroadcastAddress().equals(InetAddress.getLoopbackAddress()))
             waitForGossipToSettle();
 
-        // UDF
-        UDFRegistry.init();
-
         // Thift
         InetAddress rpcAddr = DatabaseDescriptor.getRpcAddress();
         int rpcPort = DatabaseDescriptor.getRpcPort();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/src/java/org/apache/cassandra/service/MigrationManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/MigrationManager.java 
b/src/java/org/apache/cassandra/service/MigrationManager.java
index 6e930e6..b3277ee 100644
--- a/src/java/org/apache/cassandra/service/MigrationManager.java
+++ b/src/java/org/apache/cassandra/service/MigrationManager.java
@@ -29,8 +29,6 @@ import java.util.concurrent.*;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 
-import org.apache.cassandra.config.UFMetaData;
-import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,10 +38,14 @@ import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.KSMetaData;
 import org.apache.cassandra.config.UTMetaData;
 import org.apache.cassandra.config.Schema;
+import org.apache.cassandra.cql3.functions.FunctionName;
+import org.apache.cassandra.cql3.functions.UDFunction;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.marshal.UserType;
 import org.apache.cassandra.exceptions.AlreadyExistsException;
 import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.SyntaxException;
 import org.apache.cassandra.gms.*;
 import org.apache.cassandra.io.IVersionedSerializer;
 import org.apache.cassandra.io.util.DataOutputPlus;
@@ -175,22 +177,22 @@ public class MigrationManager
             listener.onCreateUserType(ut.keyspace, ut.getNameAsString());
     }
 
-    public void notifyCreateFunction(UFMetaData uf)
+    public void notifyCreateFunction(UDFunction udf)
     {
         for (IMigrationListener listener : listeners)
-            listener.onCreateFunction(uf.namespace, uf.functionName);
+            listener.onCreateFunction(udf.name().namespace, udf.name().name);
     }
 
-    public void notifyUpdateFunction(UFMetaData uf)
+    public void notifyUpdateFunction(UDFunction udf)
     {
         for (IMigrationListener listener : listeners)
-            listener.onUpdateFunction(uf.namespace, uf.functionName);
+            listener.onUpdateFunction(udf.name().namespace, udf.name().name);
     }
 
-    public void notifyDropFunction(UFMetaData uf)
+    public void notifyDropFunction(UDFunction udf)
     {
         for (IMigrationListener listener : listeners)
-            listener.onDropFunction(uf.namespace, uf.functionName);
+            listener.onDropFunction(udf.name().namespace, udf.name().name);
     }
 
     public void notifyUpdateKeyspace(KSMetaData ksm)
@@ -372,24 +374,16 @@ public class MigrationManager
         announce(addSerializedKeyspace(UTMetaData.dropFromSchema(droppedType, 
FBUtilities.timestampMicros()), droppedType.keyspace), announceLocally);
     }
 
-    public static void announceFunctionDrop(String namespace, String 
functionName, boolean announceLocally) throws InvalidRequestException
+    public static void announceFunctionDrop(FunctionName fun, boolean 
announceLocally) throws InvalidRequestException
     {
-        Mutation mutation = 
UFMetaData.dropFunction(FBUtilities.timestampMicros(), namespace, functionName);
-        if (mutation == null)
-            throw new InvalidRequestException(String.format("Cannot drop non 
existing function '%s'.", functionName));
-
-        logger.info(String.format("Drop Function '%s::%s'", namespace, 
functionName));
-        announce(mutation, announceLocally);
+        logger.info(String.format("Drop Function '%s'", fun));
+        announce(UDFunction.dropFromSchema(FBUtilities.timestampMicros(), 
fun), announceLocally);
     }
 
-    public static void announceNewFunction(UFMetaData function, boolean 
announceLocally)
-        throws ConfigurationException
+    public static void announceNewFunction(UDFunction udf, boolean 
announceLocally)
     {
-        Mutation mutation = 
UFMetaData.createOrReplaceFunction(FBUtilities.timestampMicros(), function);
-        if (mutation == null)
-            throw new ConfigurationException(String.format("Function '%s' 
already exists.", function.qualifiedName));
-
-        logger.info(String.format("Create Function '%s'", function));
+        Mutation mutation = udf.toSchemaUpdate(FBUtilities.timestampMicros());
+        logger.info(String.format("Create Function '%s'", udf.name()));
         announce(mutation, announceLocally);
     }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/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 cb32577..760878c 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -66,6 +66,10 @@ public abstract class CQLTester
     private String currentTable;
     private final Set<String> currentTypes = new HashSet<>();
 
+    // We don't use USE_PREPARED_VALUES in the code below so some test can 
foce value preparation (if the result
+    // is not expected to be the same without preparation)
+    private boolean usePrepared = USE_PREPARED_VALUES;
+
     @BeforeClass
     public static void setUpClass() throws Throwable
     {
@@ -80,6 +84,9 @@ public abstract class CQLTester
     @After
     public void afterTest() throws Throwable
     {
+        // Restore standard behavior in case it was changed
+        usePrepared = USE_PREPARED_VALUES;
+
         if (currentTable == null)
             return;
 
@@ -162,6 +169,16 @@ public abstract class CQLTester
         return currentTable;
     }
 
+    protected void forcePreparedValues()
+    {
+        this.usePrepared = true;
+    }
+
+    protected void stopForcingPreparedValues()
+    {
+        this.usePrepared = USE_PREPARED_VALUES;
+    }
+
     protected String createType(String query)
     {
         String typeName = "type_" + seqNumber.getAndIncrement();
@@ -222,7 +239,7 @@ public abstract class CQLTester
             query = String.format(query, KEYSPACE + "." + currentTable);
 
             UntypedResultSet rs;
-            if (USE_PREPARED_VALUES)
+            if (usePrepared)
             {
                 logger.info("Executing: {} with values {}", query, 
formatAllValues(values));
                 rs = QueryProcessor.executeOnceInternal(query, 
transformValues(values));

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/test/unit/org/apache/cassandra/cql3/TypeCastTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/TypeCastTest.java 
b/test/unit/org/apache/cassandra/cql3/TypeCastTest.java
new file mode 100644
index 0000000..7b9c9a2
--- /dev/null
+++ b/test/unit/org/apache/cassandra/cql3/TypeCastTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3;
+
+import org.junit.Test;
+
+/**
+ * Type-casting is mostly using for functions and their use with functions is
+ * tested in UFTest. This is a few additional "sanity" tests.
+ */
+public class TypeCastTest extends CQLTester
+{
+    @Test
+    public void testTypeCasts() throws Throwable
+    {
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, t text, a ascii, d 
double, i int)");
+
+        // The followings is fine
+        execute("UPDATE %s SET t = 'foo' WHERE k = ?", 0);
+        execute("UPDATE %s SET t = (ascii)'foo' WHERE k = ?", 0);
+        execute("UPDATE %s SET t = (text)(ascii)'foo' WHERE k = ?", 0);
+        execute("UPDATE %s SET a = 'foo' WHERE k = ?", 0);
+        execute("UPDATE %s SET a = (ascii)'foo' WHERE k = ?", 0);
+
+        // But trying to put some explicitely type-casted text into an ascii
+        // column should be rejected (even though the text is actually ascci)
+        assertInvalid("UPDATE %s SET a = (text)'foo' WHERE k = ?", 0);
+
+        // This is also fine because integer constants works for both integer 
and float types
+        execute("UPDATE %s SET i = 3 WHERE k = ?", 0);
+        execute("UPDATE %s SET i = (int)3 WHERE k = ?", 0);
+        execute("UPDATE %s SET d = 3 WHERE k = ?", 0);
+        execute("UPDATE %s SET d = (double)3 WHERE k = ?", 0);
+
+        // But values for ints and doubles are not truly compatible (their 
binary representation differs)
+        assertInvalid("UPDATE %s SET d = (int)3 WHERE k = ?", 0);
+        assertInvalid("UPDATE %s SET i = (double)3 WHERE k = ?", 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/44fa12ec/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 ec494a6..84161b2 100644
--- a/test/unit/org/apache/cassandra/cql3/UFTest.java
+++ b/test/unit/org/apache/cassandra/cql3/UFTest.java
@@ -33,154 +33,158 @@ public class UFTest extends CQLTester
         return val != null ? (float)Math.sin(val) : null;
     }
 
-    public Float nonStaticMethod(Float val)
-    {
-        return new Float(1.0);
-    }
-
-    private static Float privateMethod(Float val)
-    {
-        return new Float(1.0);
-    }
-
-    @Test
-    public void ddlCreateFunction() throws Throwable
-    {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
-
-        execute("create function foo::cf ( input double ) returns double using 
'org.apache.cassandra.cql3.UFTest#sin'");
-        execute("drop function foo::cf");
-    }
-
-    @Test(expected = InvalidRequestException.class)
-    public void ddlCreateFunctionFail() throws Throwable
-    {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
-
-        execute("create function foo::cff ( input double ) returns double 
using 'org.apache.cassandra.cql3.UFTest#sin'");
-        execute("create function foo::cff ( input double ) returns double 
using 'org.apache.cassandra.cql3.UFTest#sin'");
-    }
-
-
-    @Test
-    public void ddlCreateIfNotExistsFunction() throws Throwable
+    public static Double badSin(Double val)
     {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
-
-        execute("create function if not exists foo::cfine ( input double ) 
returns double using 'org.apache.cassandra.cql3.UFTest#sin'");
-        execute("drop function foo::cfine");
+        return 42.0;
     }
 
-
-    @Test(expected = InvalidRequestException.class)
-    public void ddlCreateFunctionBadClass() throws Throwable
+    public static String badSinBadReturn(Double val)
     {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
-        execute("create function foo::cff ( input double ) returns double 
using 'org.apache.cassandra.cql3.DoesNotExist#doesnotexist'");
+        return "foo";
     }
 
-    @Test(expected = InvalidRequestException.class)
-    public void ddlCreateFunctionBadMethod() throws Throwable
+    public Float nonStaticMethod(Float val)
     {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
-        execute("create function foo::cff ( input double ) returns double 
using 'org.apache.cassandra.cql3.UFTest#doesnotexist'");
+        return new Float(1.0);
     }
 
-    @Test(expected = InvalidRequestException.class)
-    public void ddlCreateFunctionBadArgType() throws Throwable
+    private static Float privateMethod(Float val)
     {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
-        execute("create function foo::cff ( input text ) returns double using 
'org.apache.cassandra.cql3.UFTest#sin'");
+        return new Float(1.0);
     }
 
-    @Test(expected = InvalidRequestException.class)
-    public void ddlCreateFunctionBadReturnType() throws Throwable
+    public static String repeat(String v, Integer n)
     {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
-        execute("create function foo::cff ( input double ) returns text using 
'org.apache.cassandra.cql3.UFTest#sin'");
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < n; i++)
+            sb.append(v);
+        return sb.toString();
     }
 
-    @Test(expected = InvalidRequestException.class)
-    public void ddlCreateFunctionNonStaticMethod() throws Throwable
+    public static String overloaded(String v)
     {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
-        execute("create function foo::cff ( input float ) returns float using 
'org.apache.cassandra.cql3.UFTest#nonStaticMethod'");
+        return "f1";
     }
 
-    @Test(expected = InvalidRequestException.class)
-    public void ddlCreateFunctionNonPublicMethod() throws Throwable
+    public static String overloaded(Integer v)
     {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
-        execute("create function foo::cff ( input float ) returns float using 
'org.apache.cassandra.cql3.UFTest#privateMethod'");
+        return "f2";
     }
 
-    @Test(expected = InvalidRequestException.class)
-    public void ddlCreateIfNotExistsFunctionFail() throws Throwable
+    public static String overloaded(String v1, String v2)
     {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
-
-        execute("create function if not exists foo::cfinef ( input double ) 
returns double using 'org.apache.cassandra.cql3.UFTest#sin'");
-        execute("create function if not exists foo::cfinef ( input double ) 
returns double using 'org.apache.cassandra.cql3.UFTest#sin'");
+        return "f3";
     }
 
     @Test
-    public void ddlCreateOrReplaceFunction() throws Throwable
-    {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
+    public void testFunctionCreationAndDrop() throws Throwable
+    {
+        createTable("CREATE TABLE %s (key int PRIMARY KEY, d double)");
+
+        execute("INSERT INTO %s(key, d) VALUES (?, ?)", 1, 1d);
+        execute("INSERT INTO %s(key, d) VALUES (?, ?)", 2, 2d);
+        execute("INSERT INTO %s(key, d) VALUES (?, ?)", 3, 3d);
+
+        // creation with a bad class
+        assertInvalid("CREATE FUNCTION foo::sin1 ( input double ) RETURNS 
double USING 'org.apache.cassandra.cql3.DoesNotExist#doesnotexist'");
+        // and a good class but inexisting method
+        assertInvalid("CREATE FUNCTION foo::sin2 ( input double ) RETURNS 
double USING 'org.apache.cassandra.cql3.UFTest#doesnotexist'");
+        // with a non static method
+        assertInvalid("CREATE FUNCTION foo::sin3 ( input float ) RETURNS float 
USING 'org.apache.cassandra.cql3.UFTest#nonStaticMethod'");
+        // with a non public method
+        assertInvalid("CREATE FUNCTION foo::sin4 ( input float ) RETURNS float 
USING 'org.apache.cassandra.cql3.UFTest#privateMethod'");
+
+        // creation with bad argument types
+        assertInvalid("CREATE FUNCTION foo::sin5 ( input text ) RETURNS double 
USING 'org.apache.cassandra.cql3.UFTest#sin'");
+        // with bad return types
+        assertInvalid("CREATE FUNCTION foo::sin6 ( input double ) RETURNS text 
USING 'org.apache.cassandra.cql3.UFTest#sin'");
+
+        // simple creation
+        execute("CREATE FUNCTION foo::sin ( input double ) RETURNS double 
USING 'org.apache.cassandra.cql3.UFTest#sin'");
+        // check we can't recreate the same function
+        assertInvalid("CREATE FUNCTION foo::sin ( input double ) RETURNS 
double USING 'org.apache.cassandra.cql3.UFTest#sin'");
+        // but that it doesn't complay with "IF NOT EXISTS"
+        execute("CREATE FUNCTION IF NOT EXISTS foo::sin ( input double ) 
RETURNS double USING 'org.apache.cassandra.cql3.UFTest#sin'");
+
+        // Validate that it works as expected
+        assertRows(execute("SELECT key, foo::sin(d) FROM %s"),
+            row(1, Math.sin(1d)),
+            row(2, Math.sin(2d)),
+            row(3, Math.sin(3d))
+        );
 
-        execute("create function foo::corf ( input double ) returns double 
using 'org.apache.cassandra.cql3.UFTest#sin'");
-        execute("create or replace function foo::corf ( input double ) returns 
double using 'org.apache.cassandra.cql3.UFTest#sin'");
-    }
+        // Replace the method with incompatible return type
+        assertInvalid("CREATE OR REPLACE FUNCTION foo::sin ( input double ) 
RETURNS text USING 'org.apache.cassandra.cql3.UFTest#badSinBadReturn'");
+        // proper replacement
+        execute("CREATE OR REPLACE FUNCTION foo::sin ( input double ) RETURNS 
double USING 'org.apache.cassandra.cql3.UFTest#badSin'");
 
-    @Test(expected = InvalidRequestException.class)
-    public void ddlDropNonExistingFunction() throws Throwable
-    {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
+        // Validate the method as been replaced
+        assertRows(execute("SELECT key, foo::sin(d) FROM %s"),
+            row(1, 42.0),
+            row(2, 42.0),
+            row(3, 42.0)
+        );
 
-        execute("drop function foo::dnef");
-    }
+        // same function but without namespace
+        execute("CREATE FUNCTION sin ( input double ) RETURNS double USING 
'org.apache.cassandra.cql3.UFTest#sin'");
+        assertRows(execute("SELECT key, sin(d) FROM %s"),
+            row(1, Math.sin(1d)),
+            row(2, Math.sin(2d)),
+            row(3, Math.sin(3d))
+        );
 
-    @Test
-    public void ddlDropIfExistsNonExistingFunction() throws Throwable
-    {
-        createTable("CREATE TABLE %s (key int primary key, val double)"); // 
not used, but required by CQLTester
+        // Drop with and without namespace
+        execute("DROP FUNCTION foo::sin");
+        execute("DROP FUNCTION sin");
 
-        execute("drop function if exists foo::dienef");
+        // Drop unexisting function
+        assertInvalid("DROP FUNCTION foo::sin");
+        // but don't complain with "IF EXISTS"
+        execute("DROP FUNCTION IF EXISTS foo::sin");
     }
 
     @Test
-    public void namespaceUserFunctions() throws Throwable
+    public void testFunctionExecution() throws Throwable
     {
-        createTable("CREATE TABLE %s (key int primary key, val double)");
+        createTable("CREATE TABLE %s (v text PRIMARY KEY)");
 
-        execute("create or replace function math::sin ( input double ) returns 
double using 'org.apache.cassandra.cql3.UFTest'");
+        execute("INSERT INTO %s(v) VALUES (?)", "aaa");
 
-        execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d);
-        execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d);
-        execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d);
+        execute("CREATE FUNCTION repeat (v text, n int) RETURNS text USING 
'org.apache.cassandra.cql3.UFTest#repeat'");
 
-        assertRows(execute("SELECT key, val, math::sin(val) FROM %s"),
-                   row(1, 1d, Math.sin(1d)),
-                   row(2, 2d, Math.sin(2d)),
-                   row(3, 3d, Math.sin(3d))
-        );
+        assertRows(execute("SELECT v FROM %s WHERE v=repeat(?, ?)", "a", 3), 
row("aaa"));
+        assertEmpty(execute("SELECT v FROM %s WHERE v=repeat(?, ?)", "a", 2));
     }
 
     @Test
-    public void nonNamespaceUserFunctions() throws Throwable
+    public void testFunctionOverloading() throws Throwable
     {
-        createTable("CREATE TABLE %s (key int primary key, val double)");
+        createTable("CREATE TABLE %s (k text PRIMARY KEY, v int)");
 
-        execute("create or replace function sin ( input double ) returns 
double using 'org.apache.cassandra.cql3.UFTest'");
+        execute("INSERT INTO %s(k, v) VALUES (?, ?)", "f2", 1);
 
-        execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d);
-        execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d);
-        execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d);
+        execute("CREATE FUNCTION overloaded(v varchar) RETURNS text USING 
'org.apache.cassandra.cql3.UFTest'");
+        execute("CREATE OR REPLACE FUNCTION overloaded(i int) RETURNS text 
USING 'org.apache.cassandra.cql3.UFTest'");
+        execute("CREATE OR REPLACE FUNCTION overloaded(v1 text, v2 text) 
RETURNS text USING 'org.apache.cassandra.cql3.UFTest'");
+        execute("CREATE OR REPLACE FUNCTION overloaded(v ascii) RETURNS text 
USING 'org.apache.cassandra.cql3.UFTest'");
 
-        assertRows(execute("SELECT key, val, sin(val) FROM %s"),
-                   row(1, 1d, Math.sin(1d)),
-                   row(2, 2d, Math.sin(2d)),
-                   row(3, 3d, Math.sin(3d))
+        // text == varchar, so this should be considered as a duplicate
+        assertInvalid("CREATE FUNCTION overloaded(v varchar) RETURNS text 
USING 'org.apache.cassandra.cql3.UFTest'");
+
+        assertRows(execute("SELECT overloaded(k), overloaded(v), overloaded(k, 
k) FROM %s"),
+            row("f1", "f2", "f3")
         );
+
+        forcePreparedValues();
+        // This shouldn't work if we use preparation since there no way to 
know which overload to use
+        assertInvalid("SELECT v FROM %s WHERE k = overloaded(?)", "foo");
+        stopForcingPreparedValues();
+
+        // but those should since we specifically cast
+        assertEmpty(execute("SELECT v FROM %s WHERE k = overloaded((text)?)", 
"foo"));
+        assertRows(execute("SELECT v FROM %s WHERE k = overloaded((int)?)", 
3), row(1));
+        assertEmpty(execute("SELECT v FROM %s WHERE k = overloaded((ascii)?)", 
"foo"));
+        // And since varchar == text, this should work too
+        assertEmpty(execute("SELECT v FROM %s WHERE k = 
overloaded((varchar)?)", "foo"));
     }
 }

Reply via email to