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;" +
+ "$$");
+
+ }
}