You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sn...@apache.org on 2016/02/04 20:41:07 UTC

cassandra git commit: Generic Java UDF types

Repository: cassandra
Updated Branches:
  refs/heads/trunk 1925a014b -> 64cfcf055


Generic Java UDF types

patch by Robert Stupp; reviewed by DOAN DuyHai for CASSANDRA-10819


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

Branch: refs/heads/trunk
Commit: 64cfcf05568e1d0216ee7b1d8719417291dd752a
Parents: 1925a01
Author: Robert Stupp <sn...@snazy.de>
Authored: Thu Feb 4 20:31:37 2016 +0100
Committer: Robert Stupp <sn...@snazy.de>
Committed: Thu Feb 4 20:31:37 2016 +0100

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../cql3/functions/JavaBasedUDFunction.java     | 28 +++++++-----
 .../cql3/functions/ScriptBasedUDFunction.java   |  2 +-
 .../cassandra/cql3/functions/UDHelper.java      | 16 ++++---
 .../cql3/validation/entities/UFTest.java        | 48 +++++++++++++++++++-
 5 files changed, 75 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/64cfcf05/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index ffe50a3..3fcca42 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.4
+ * Generic Java UDF types (CASSANDRA-10819)
  * cqlsh: Include sub-second precision in timestamps by default (CASSANDRA-10428)
  * Set javac encoding to utf-8 (CASSANDRA-11077)
  * Integrate SASI index into Cassandra (CASSANDRA-10661)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/64cfcf05/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java b/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java
index c61e72e..b1dd9f9 100644
--- a/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java
+++ b/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java
@@ -35,8 +35,11 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.io.ByteStreams;
+import com.google.common.reflect.TypeToken;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -57,10 +60,12 @@ import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
 import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
 
-final class JavaBasedUDFunction extends UDFunction
+public final class JavaBasedUDFunction extends UDFunction
 {
     private static final String BASE_PACKAGE = "org.apache.cassandra.cql3.udf.gen";
 
+    private static final Pattern JAVA_LANG_PREFIX = Pattern.compile("\\bjava\\.lang\\.");
+
     static final Logger logger = LoggerFactory.getLogger(JavaBasedUDFunction.class);
 
     private static final AtomicInteger classSequence = new AtomicInteger();
@@ -185,9 +190,9 @@ final class JavaBasedUDFunction extends UDFunction
               returnType, UDHelper.driverType(returnType), calledOnNullInput, "java", body);
 
         // javaParamTypes is just the Java representation for argTypes resp. argDataTypes
-        Class<?>[] javaParamTypes = UDHelper.javaTypes(argDataTypes, calledOnNullInput);
+        TypeToken<?>[] javaParamTypes = UDHelper.typeTokens(argDataTypes, calledOnNullInput);
         // javaReturnType is just the Java representation for returnType resp. returnDataType
-        Class<?> javaReturnType = UDHelper.asJavaClass(returnDataType);
+        TypeToken<?> javaReturnType = UDHelper.asTypeToken(returnDataType);
 
         // put each UDF in a separate package to prevent cross-UDF code access
         String pkgName = BASE_PACKAGE + '.' + generateClassName(name, 'p');
@@ -244,7 +249,7 @@ final class JavaBasedUDFunction extends UDFunction
         {
             EcjCompilationUnit compilationUnit = new EcjCompilationUnit(javaSource, targetClassName);
 
-            org.eclipse.jdt.internal.compiler.Compiler compiler = new Compiler(compilationUnit,
+            Compiler compiler = new Compiler(compilationUnit,
                                                                                errorHandlingPolicy,
                                                                                compilerOptions,
                                                                                compilationUnit,
@@ -392,13 +397,14 @@ final class JavaBasedUDFunction extends UDFunction
         return sb.toString();
     }
 
-    private static String javaSourceName(Class<?> type)
+    @VisibleForTesting
+    public static String javaSourceName(TypeToken<?> type)
     {
-        String n = type.getName();
-        return n.startsWith("java.lang.") ? type.getSimpleName() : n;
+        String n = type.toString();
+        return JAVA_LANG_PREFIX.matcher(n).replaceAll("");
     }
 
-    private static String generateArgumentList(Class<?>[] paramTypes, List<ColumnIdentifier> argNames)
+    private static String generateArgumentList(TypeToken<?>[] paramTypes, List<ColumnIdentifier> argNames)
     {
         // initial builder size can just be a guess (prevent temp object allocations)
         StringBuilder code = new StringBuilder(32 * paramTypes.length);
@@ -413,7 +419,7 @@ final class JavaBasedUDFunction extends UDFunction
         return code.toString();
     }
 
-    private static String generateArguments(Class<?>[] paramTypes, List<ColumnIdentifier> argNames)
+    private static String generateArguments(TypeToken<?>[] paramTypes, List<ColumnIdentifier> argNames)
     {
         StringBuilder code = new StringBuilder(64 * paramTypes.length);
         for (int i = 0; i < paramTypes.length; i++)
@@ -433,9 +439,9 @@ final class JavaBasedUDFunction extends UDFunction
         return code.toString();
     }
 
-    private static String composeMethod(Class<?> type)
+    private static String composeMethod(TypeToken<?> type)
     {
-        return (type.isPrimitive()) ? ("super.compose_" + type.getName()) : "super.compose";
+        return (type.isPrimitive()) ? ("super.compose_" + type.getRawType().getName()) : "super.compose";
     }
 
     // Java source UDFs are a very simple compilation task, which allows us to let one class implement

http://git-wip-us.apache.org/repos/asf/cassandra/blob/64cfcf05/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java b/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java
index 4ffb992..bf28663 100644
--- a/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java
+++ b/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java
@@ -189,7 +189,7 @@ final class ScriptBasedUDFunction extends UDFunction
         if (result == null)
             return null;
 
-        Class<?> javaReturnType = UDHelper.asJavaClass(returnDataType);
+        Class<?> javaReturnType = UDHelper.asTypeToken(returnDataType).getRawType();
         Class<?> resultType = result.getClass();
         if (!javaReturnType.isAssignableFrom(resultType))
         {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/64cfcf05/src/java/org/apache/cassandra/cql3/functions/UDHelper.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/UDHelper.java b/src/java/org/apache/cassandra/cql3/functions/UDHelper.java
index cc62c84..6237369 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UDHelper.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UDHelper.java
@@ -23,6 +23,8 @@ import java.lang.reflect.Method;
 import java.nio.ByteBuffer;
 import java.util.List;
 
+import com.google.common.reflect.TypeToken;
+
 import com.datastax.driver.core.CodecRegistry;
 import com.datastax.driver.core.DataType;
 import com.datastax.driver.core.ProtocolVersion;
@@ -68,15 +70,16 @@ public final class UDHelper
      * @param calledOnNullInput whether to allow {@code null} as an argument value
      * @return array of same size with UDF arguments
      */
-    public static Class<?>[] javaTypes(DataType[] dataTypes, boolean calledOnNullInput)
+    public static TypeToken<?>[] typeTokens(DataType[] dataTypes, boolean calledOnNullInput)
     {
-        Class<?>[] paramTypes = new Class[dataTypes.length];
+        TypeToken<?>[] paramTypes = new TypeToken[dataTypes.length];
         for (int i = 0; i < paramTypes.length; i++)
         {
-            Class<?> clazz = asJavaClass(dataTypes[i]);
+            TypeToken<?> typeToken = asTypeToken(dataTypes[i]);
             if (!calledOnNullInput)
             {
                 // only care about classes that can be used in a data type
+                Class<?> clazz = typeToken.getRawType();
                 if (clazz == Integer.class)
                     clazz = int.class;
                 else if (clazz == Long.class)
@@ -91,8 +94,9 @@ public final class UDHelper
                     clazz = double.class;
                 else if (clazz == Boolean.class)
                     clazz = boolean.class;
+                typeToken = TypeToken.of(clazz);
             }
-            paramTypes[i] = clazz;
+            paramTypes[i] = typeToken;
         }
         return paramTypes;
     }
@@ -149,9 +153,9 @@ public final class UDHelper
         return codec.serialize(value, ProtocolVersion.fromInt(protocolVersion));
     }
 
-    public static Class<?> asJavaClass(DataType dataType)
+    public static TypeToken<?> asTypeToken(DataType dataType)
     {
-        return codecFor(dataType).getJavaType().getRawType();
+        return codecFor(dataType).getJavaType();
     }
 
     public static boolean isNullOrEmpty(AbstractType<?> type, ByteBuffer bb)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/64cfcf05/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java
index cc0e806..f482d54 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java
@@ -18,6 +18,7 @@
 package org.apache.cassandra.cql3.validation.entities;
 
 import java.nio.ByteBuffer;
+import java.security.AccessControlException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
@@ -27,21 +28,22 @@ import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.UUID;
-import java.security.AccessControlException;
 
+import com.google.common.reflect.TypeToken;
 import org.junit.Assert;
 import org.junit.Test;
 
 import com.datastax.driver.core.*;
 import com.datastax.driver.core.exceptions.InvalidQueryException;
+import org.apache.cassandra.config.Config;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.cql3.CQL3Type;
 import org.apache.cassandra.cql3.CQLTester;
 import org.apache.cassandra.cql3.QueryProcessor;
 import org.apache.cassandra.cql3.UntypedResultSet;
-import org.apache.cassandra.config.Config;
 import org.apache.cassandra.cql3.functions.FunctionName;
+import org.apache.cassandra.cql3.functions.JavaBasedUDFunction;
 import org.apache.cassandra.cql3.functions.UDFunction;
 import org.apache.cassandra.cql3.functions.UDHelper;
 import org.apache.cassandra.db.marshal.CollectionType;
@@ -59,6 +61,15 @@ import org.apache.cassandra.utils.UUIDGen;
 public class UFTest extends CQLTester
 {
     @Test
+    public void testJavaSourceName()
+    {
+        Assert.assertEquals("String", JavaBasedUDFunction.javaSourceName(TypeToken.of(String.class)));
+        Assert.assertEquals("java.util.Map<Integer, String>", JavaBasedUDFunction.javaSourceName(TypeTokens.mapOf(Integer.class, String.class)));
+        Assert.assertEquals("com.datastax.driver.core.UDTValue", JavaBasedUDFunction.javaSourceName(TypeToken.of(UDTValue.class)));
+        Assert.assertEquals("java.util.Set<com.datastax.driver.core.UDTValue>", JavaBasedUDFunction.javaSourceName(TypeTokens.setOf(UDTValue.class)));
+    }
+
+    @Test
     public void testNonExistingOnes() throws Throwable
     {
         assertInvalidThrowMessage("Cannot drop non existing function", InvalidRequestException.class, "DROP FUNCTION " + KEYSPACE + ".func_does_not_exist");
@@ -2485,4 +2496,37 @@ public class UFTest extends CQLTester
             }
         }
     }
+
+    @Test
+    public void testArgumentGenerics() throws Throwable
+    {
+        createTable("CREATE TABLE %s (key int primary key, sval text, aval ascii, bval blob, empty_int int)");
+
+        String typeName = createType("CREATE TYPE %s (txt text, i int)");
+
+        String f = createFunction(KEYSPACE, "text",
+                                  "CREATE OR REPLACE FUNCTION %s("                 +
+                                  "  listText list<text>,"                         +
+                                  "  setText set<text>,"                           +
+                                  "  mapTextInt map<text, int>,"                   +
+                                  "  mapListTextSetInt map<frozen<list<text>>, frozen<set<int>>>," +
+                                  "  mapTextTuple map<text, frozen<tuple<int, text>>>," +
+                                  "  mapTextType map<text, frozen<" + typeName + ">>" +
+                                  ") "                                             +
+                                  "CALLED ON NULL INPUT "                          +
+                                  "RETURNS map<frozen<list<text>>, frozen<set<int>>> " +
+                                  "LANGUAGE JAVA\n"                                +
+                                  "AS $$" +
+                                  "     for (String s : listtext) {};" +
+                                  "     for (String s : settext) {};" +
+                                  "     for (String s : maptextint.keySet()) {};" +
+                                  "     for (Integer s : maptextint.values()) {};" +
+                                  "     for (java.util.List<String> l : maplisttextsetint.keySet()) {};" +
+                                  "     for (java.util.Set<Integer> s : maplisttextsetint.values()) {};" +
+                                  "     for (com.datastax.driver.core.TupleValue t : maptexttuple.values()) {};" +
+                                  "     for (com.datastax.driver.core.UDTValue u : maptexttype.values()) {};" +
+                                  "     return maplisttextsetint;" +
+                                  "$$");
+
+    }
 }