You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@drill.apache.org by pa...@apache.org on 2016/10/18 23:45:27 UTC
[07/11] drill git commit: DRILL-4726: Dynamic UDF Support
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/main/java/org/apache/drill/exec/testing/store/NoWriteLocalStore.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/testing/store/NoWriteLocalStore.java b/exec/java-exec/src/main/java/org/apache/drill/exec/testing/store/NoWriteLocalStore.java
index ff14f6d..58ec3ea 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/testing/store/NoWriteLocalStore.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/testing/store/NoWriteLocalStore.java
@@ -20,17 +20,32 @@ package org.apache.drill.exec.testing.store;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
+import org.apache.drill.common.concurrent.AutoCloseableLock;
+import org.apache.drill.exec.exception.VersionMismatchException;
import org.apache.drill.exec.store.sys.BasePersistentStore;
import org.apache.drill.exec.store.sys.PersistentStoreMode;
+import org.apache.drill.exec.store.sys.store.DataChangeVersion;
public class NoWriteLocalStore<V> extends BasePersistentStore<V> {
+ private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
+ private final AutoCloseableLock readLock = new AutoCloseableLock(readWriteLock.readLock());
+ private final AutoCloseableLock writeLock = new AutoCloseableLock(readWriteLock.writeLock());
private final ConcurrentMap<String, V> store = Maps.newConcurrentMap();
+ private final AtomicInteger version = new AtomicInteger();
+ @Override
public void delete(final String key) {
- store.remove(key);
+ try (AutoCloseableLock lock = writeLock.open()) {
+ store.remove(key);
+ version.incrementAndGet();
+ }
}
@Override
@@ -40,27 +55,59 @@ public class NoWriteLocalStore<V> extends BasePersistentStore<V> {
@Override
public V get(final String key) {
- return store.get(key);
+ return get(key, null);
+ }
+
+ @Override
+ public V get(final String key, final DataChangeVersion dataChangeVersion) {
+ try (AutoCloseableLock lock = readLock.open()) {
+ if (dataChangeVersion != null) {
+ dataChangeVersion.setVersion(version.get());
+ }
+ return store.get(key);
+ }
}
@Override
public void put(final String key, final V value) {
- store.put(key, value);
+ put(key, value, null);
+ }
+
+ @Override
+ public void put(final String key, final V value, final DataChangeVersion dataChangeVersion) {
+ try (AutoCloseableLock lock = writeLock.open()) {
+ if (dataChangeVersion != null && dataChangeVersion.getVersion() != version.get()) {
+ throw new VersionMismatchException("Version mismatch detected", dataChangeVersion.getVersion());
+ }
+ store.put(key, value);
+ version.incrementAndGet();
+ }
}
@Override
public boolean putIfAbsent(final String key, final V value) {
- final V old = store.putIfAbsent(key, value);
- return value != old;
+ try (AutoCloseableLock lock = writeLock.open()) {
+ final V old = store.putIfAbsent(key, value);
+ if (old == null) {
+ version.incrementAndGet();
+ return true;
+ }
+ return false;
+ }
}
@Override
public Iterator<Map.Entry<String, V>> getRange(final int skip, final int take) {
- return Iterables.limit(Iterables.skip(store.entrySet(), skip), take).iterator();
+ try (AutoCloseableLock lock = readLock.open()) {
+ return Iterables.limit(Iterables.skip(store.entrySet(), skip), take).iterator();
+ }
}
@Override
public void close() throws Exception {
- store.clear();
+ try (AutoCloseableLock lock = writeLock.open()) {
+ store.clear();
+ version.set(0);
+ }
}
}
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/main/java/org/apache/drill/exec/util/JarUtil.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/util/JarUtil.java b/exec/java-exec/src/main/java/org/apache/drill/exec/util/JarUtil.java
new file mode 100644
index 0000000..3157223
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/util/JarUtil.java
@@ -0,0 +1,33 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.drill.exec.util;
+
+public class JarUtil {
+
+ /**
+ * Generates source jar name based on binary jar name.
+ * It is expected binary and source have standard naming convention.
+ *
+ * @param binary binary jar name
+ * @return source jar name
+ */
+ public static String getSourceName(String binary) {
+ return binary.replace(".jar", "-sources.jar");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/main/resources/drill-module.conf
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/resources/drill-module.conf b/exec/java-exec/src/main/resources/drill-module.conf
index d8f4759b..60dcf15 100644
--- a/exec/java-exec/src/main/resources/drill-module.conf
+++ b/exec/java-exec/src/main/resources/drill-module.conf
@@ -45,6 +45,12 @@ drill.client: {
supports-complex-types: true
}
+// Directory is used as base for temporary storage of Dynamic UDF jars.
+// Set this property if you want to have custom temporary directory, instead of generated at runtime.
+// By default ${DRILL_TMP_DIR} is used if set.
+// drill.tmp-dir: "/tmp"
+// drill.tmp-dir: ${?DRILL_TMP_DIR}
+
drill.exec: {
cluster-id: "drillbits1"
rpc: {
@@ -189,6 +195,29 @@ drill.exec: {
debug: {
return_error_for_failure_in_cancelled_fragments: false
}
+ udf: {
+ retry-attempts: 5,
+ directory: {
+ // Set this property if custom file system should be used to create remote directories, ex: fs: "file:///".
+ // fs: "",
+ // Set this property if custom absolute root should be used for remote directories, ex: root: "/app/drill".
+ // root: "",
+
+ // Relative base directory for all udf directories (local and remote).
+ base: ${drill.exec.zk.root}"/udf",
+
+ // Relative path to local udf directory.
+ // Generated temporary directory is used as root unless ${DRILL_TMP_DIR} or ${drill.tmp-dir} is set.
+ local: ${drill.exec.udf.directory.base}"/local"
+
+ // Relative path to all remote udf directories.
+ // Directories are created under default file system unless ${drill.exec.udf.directory.fs} is set.
+ // User home directory is used as root unless ${drill.exec.udf.directory.root} is set.
+ staging: ${drill.exec.udf.directory.base}"/staging",
+ registry: ${drill.exec.udf.directory.base}"/registry",
+ tmp: ${drill.exec.udf.directory.base}"/tmp",
+ }
+ }
}
drill.jdbc: {
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/java/org/apache/drill/BaseTestQuery.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/java/org/apache/drill/BaseTestQuery.java b/exec/java-exec/src/test/java/org/apache/drill/BaseTestQuery.java
index 3eded52..e528d0e 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/BaseTestQuery.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/BaseTestQuery.java
@@ -64,7 +64,6 @@ import org.junit.runner.Description;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
-import com.google.common.io.Files;
import com.google.common.io.Resources;
public class BaseTestQuery extends ExecTest {
@@ -423,19 +422,6 @@ public class BaseTestQuery extends ExecTest {
return file.getPath();
}
- /**
- * Create a temp directory to store the given <i>dirName</i>
- * @param dirName
- * @return Full path including temp parent directory and given directory name.
- */
- public static String getTempDir(final String dirName) {
- final File dir = Files.createTempDir();
- dir.deleteOnExit();
-
- return dir.getAbsolutePath() + File.separator + dirName;
- }
-
-
protected static void setSessionOption(final String option, final String value) {
try {
runSQL(String.format("alter session set `%s` = %s", option, value));
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/java/org/apache/drill/TestDynamicUDFSupport.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/java/org/apache/drill/TestDynamicUDFSupport.java b/exec/java-exec/src/test/java/org/apache/drill/TestDynamicUDFSupport.java
new file mode 100644
index 0000000..ae73a3d
--- /dev/null
+++ b/exec/java-exec/src/test/java/org/apache/drill/TestDynamicUDFSupport.java
@@ -0,0 +1,801 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.drill;
+
+import com.google.common.collect.Lists;
+import mockit.Deencapsulation;
+import org.apache.drill.common.config.CommonConstants;
+import org.apache.drill.common.config.DrillConfig;
+import org.apache.drill.common.exceptions.UserRemoteException;
+import org.apache.drill.common.util.TestTools;
+import org.apache.drill.exec.exception.VersionMismatchException;
+import org.apache.drill.exec.expr.fn.FunctionImplementationRegistry;
+import org.apache.drill.exec.expr.fn.registry.LocalFunctionRegistry;
+import org.apache.drill.exec.expr.fn.registry.RemoteFunctionRegistry;
+import org.apache.drill.exec.proto.UserBitShared.Jar;
+import org.apache.drill.exec.proto.UserBitShared.Registry;
+import org.apache.drill.exec.server.DrillbitContext;
+import org.apache.drill.exec.store.sys.store.DataChangeVersion;
+import org.apache.drill.exec.util.JarUtil;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TestDynamicUDFSupport extends BaseTestQuery {
+
+ private static final File jars = new File(TestTools.getWorkingPath() + "/src/test/resources/jars");
+ private static final String default_binary_name = "DrillUDF-1.0.jar";
+ private static final String default_source_name = JarUtil.getSourceName(default_binary_name);
+
+ @Rule
+ public final TemporaryFolder base = new TemporaryFolder();
+
+ @Before
+ public void setup() {
+ Properties overrideProps = new Properties();
+ overrideProps.setProperty("drill.exec.udf.directory.root", base.getRoot().getPath());
+ updateTestCluster(1, DrillConfig.create(overrideProps));
+ }
+
+ @Test
+ public void testSyntax() throws Exception {
+ test("create function using jar 'jar_name.jar'");
+ test("drop function using jar 'jar_name.jar'");
+ }
+
+ @Test
+ public void testEnableDynamicSupport() throws Exception {
+ try {
+ test("alter system set `exec.udf.enable_dynamic_support` = true");
+ test("create function using jar 'jar_name.jar'");
+ test("drop function using jar 'jar_name.jar'");
+ } finally {
+ test("alter system reset `exec.udf.enable_dynamic_support`");
+ }
+ }
+
+ @Test
+ public void testDisableDynamicSupport() throws Exception {
+ try {
+ test("alter system set `exec.udf.enable_dynamic_support` = false");
+ String[] actions = new String[] {"create", "drop"};
+ String query = "%s function using jar 'jar_name.jar'";
+ for (String action : actions) {
+ try {
+ test(query, action);
+ } catch (UserRemoteException e) {
+ assertThat(e.getMessage(), containsString("Dynamic UDFs support is disabled."));
+ }
+ }
+ } finally {
+ test("alter system reset `exec.udf.enable_dynamic_support`");
+ }
+ }
+
+ @Test
+ public void testAbsentBinaryInStaging() throws Exception {
+ Path staging = getDrillbitContext().getRemoteFunctionRegistry().getStagingArea();
+
+ String summary = String.format("File %s does not exist", new Path(staging, default_binary_name).toUri().getPath());
+
+ testBuilder()
+ .sqlQuery("create function using jar '%s'", default_binary_name)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, summary)
+ .go();
+ }
+
+ @Test
+ public void testAbsentSourceInStaging() throws Exception {
+ Path staging = getDrillbitContext().getRemoteFunctionRegistry().getStagingArea();
+ copyJar(getDrillbitContext().getRemoteFunctionRegistry().getFs(), new Path(jars.toURI()), staging, default_binary_name);
+
+ String summary = String.format("File %s does not exist", new Path(staging, default_source_name).toUri().getPath());
+
+ testBuilder()
+ .sqlQuery("create function using jar '%s'", default_binary_name)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, summary)
+ .go();
+ }
+
+ @Test
+ public void testJarWithoutMarkerFile() throws Exception {
+ String jarWithNoMarkerFile = "DrillUDF_NoMarkerFile-1.0.jar";
+ copyJarsToStagingArea(jarWithNoMarkerFile, JarUtil.getSourceName(jarWithNoMarkerFile));
+
+ String summary = "Marker file %s is missing in %s";
+
+ testBuilder()
+ .sqlQuery("create function using jar '%s'", jarWithNoMarkerFile)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, String.format(summary, CommonConstants.DRILL_JAR_MARKER_FILE_RESOURCE_PATHNAME, jarWithNoMarkerFile))
+ .go();
+ }
+
+ @Test
+ public void testJarWithoutFunctions() throws Exception {
+ String jarWithNoFunctions = "DrillUDF_Empty-1.0.jar";
+ copyJarsToStagingArea(jarWithNoFunctions, JarUtil.getSourceName(jarWithNoFunctions));
+
+ String summary = "Jar %s does not contain functions";
+
+ testBuilder()
+ .sqlQuery("create function using jar '%s'", jarWithNoFunctions)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, String.format(summary, jarWithNoFunctions))
+ .go();
+ }
+
+ @Test
+ public void testSuccessfulCreate() throws Exception {
+ copyDefaultJarsToStagingArea();
+
+ String summary = "The following UDFs in jar %s have been registered:\n" +
+ "[custom_lower(VARCHAR-REQUIRED)]";
+
+ testBuilder()
+ .sqlQuery("create function using jar '%s'", default_binary_name)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(true, String.format(summary, default_binary_name))
+ .go();
+
+ RemoteFunctionRegistry remoteFunctionRegistry = getDrillbitContext().getRemoteFunctionRegistry();
+ FileSystem fs = remoteFunctionRegistry.getFs();
+
+ assertFalse("Staging area should be empty", fs.listFiles(remoteFunctionRegistry.getStagingArea(), false).hasNext());
+ assertFalse("Temporary area should be empty", fs.listFiles(remoteFunctionRegistry.getTmpArea(), false).hasNext());
+
+ assertTrue("Binary should be present in registry area",
+ fs.exists(new Path(remoteFunctionRegistry.getRegistryArea(), default_binary_name)));
+ assertTrue("Source should be present in registry area",
+ fs.exists(new Path(remoteFunctionRegistry.getRegistryArea(), default_source_name)));
+
+ Registry registry = remoteFunctionRegistry.getRegistry();
+ assertEquals("Registry should contain one jar", registry.getJarList().size(), 1);
+ assertEquals(registry.getJar(0).getName(), default_binary_name);
+ }
+
+ @Test
+ public void testDuplicatedJarInRemoteRegistry() throws Exception {
+ copyDefaultJarsToStagingArea();
+ test("create function using jar '%s'", default_binary_name);
+ copyDefaultJarsToStagingArea();
+
+ String summary = "Jar with %s name has been already registered";
+
+ testBuilder()
+ .sqlQuery("create function using jar '%s'", default_binary_name)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, String.format(summary, default_binary_name))
+ .go();
+ }
+
+ @Test
+ public void testDuplicatedJarInLocalRegistry() throws Exception {
+ copyDefaultJarsToStagingArea();
+ test("create function using jar '%s'", default_binary_name);
+ test("select custom_lower('A') from (values(1))");
+ copyDefaultJarsToStagingArea();
+
+ String summary = "Jar with %s name has been already registered";
+
+ testBuilder()
+ .sqlQuery("create function using jar '%s'", default_binary_name)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, String.format(summary, default_binary_name))
+ .go();
+ }
+
+ @Test
+ public void testDuplicatedFunctionsInRemoteRegistry() throws Exception {
+ String jarWithDuplicate = "DrillUDF_Copy-1.0.jar";
+ copyDefaultJarsToStagingArea();
+ test("create function using jar '%s'", default_binary_name);
+ copyJarsToStagingArea(jarWithDuplicate, JarUtil.getSourceName(jarWithDuplicate));
+
+ String summary = "Found duplicated function in %s: custom_lower(VARCHAR-REQUIRED)";
+
+ testBuilder()
+ .sqlQuery("create function using jar '%s'", jarWithDuplicate)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, String.format(summary, default_binary_name))
+ .go();
+ }
+
+ @Test
+ public void testDuplicatedFunctionsInLocalRegistry() throws Exception {
+ String jarWithDuplicate = "DrillUDF_DupFunc-1.0.jar";
+ copyJarsToStagingArea(jarWithDuplicate, JarUtil.getSourceName(jarWithDuplicate));
+
+ String summary = "Found duplicated function in %s: lower(VARCHAR-REQUIRED)";
+
+ testBuilder()
+ .sqlQuery("create function using jar '%s'", jarWithDuplicate)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, String.format(summary, LocalFunctionRegistry.BUILT_IN))
+ .go();
+ }
+
+ @Test
+ public void testExceedRetryAttemptsDuringRegistration() throws Exception {
+ RemoteFunctionRegistry remoteFunctionRegistry = spyRemoteFunctionRegistry();
+ copyDefaultJarsToStagingArea();
+
+ doThrow(new VersionMismatchException("Version mismatch detected", 1))
+ .when(remoteFunctionRegistry).updateRegistry(any(Registry.class), any(DataChangeVersion.class));
+
+ String summary = "Failed to update remote function registry. Exceeded retry attempts limit.";
+
+ testBuilder()
+ .sqlQuery("create function using jar '%s'", default_binary_name)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, summary)
+ .go();
+
+ verify(remoteFunctionRegistry, times(remoteFunctionRegistry.getRetryAttempts() + 1))
+ .updateRegistry(any(Registry.class), any(DataChangeVersion.class));
+ }
+
+ @Test
+ public void testExceedRetryAttemptsDuringUnregistration() throws Exception {
+ RemoteFunctionRegistry remoteFunctionRegistry = spyRemoteFunctionRegistry();
+ copyDefaultJarsToStagingArea();
+ test("create function using jar '%s'", default_binary_name);
+
+ reset(remoteFunctionRegistry);
+ doThrow(new VersionMismatchException("Version mismatch detected", 1))
+ .when(remoteFunctionRegistry).updateRegistry(any(Registry.class), any(DataChangeVersion.class));
+
+ String summary = "Failed to update remote function registry. Exceeded retry attempts limit.";
+
+ testBuilder()
+ .sqlQuery("drop function using jar '%s'", default_binary_name)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, summary)
+ .go();
+
+ verify(remoteFunctionRegistry, times(remoteFunctionRegistry.getRetryAttempts() + 1))
+ .updateRegistry(any(Registry.class), any(DataChangeVersion.class));
+ }
+
+ @Test
+ public void testLazyInit() throws Exception {
+ try {
+ test("select custom_lower('A') from (values(1))");
+ } catch (UserRemoteException e){
+ assertThat(e.getMessage(), containsString("No match found for function signature custom_lower(<CHARACTER>)"));
+ }
+
+ copyDefaultJarsToStagingArea();
+ test("create function using jar '%s'", default_binary_name);
+ testBuilder()
+ .sqlQuery("select custom_lower('A') as res from (values(1))")
+ .unOrdered()
+ .baselineColumns("res")
+ .baselineValues("a")
+ .go();
+
+ Path localUdfDirPath = Deencapsulation.getField(getDrillbitContext().getFunctionImplementationRegistry(), "localUdfDir");
+ File localUdfDir = new File(localUdfDirPath.toUri().getPath());
+
+ assertTrue("Binary should exist in local udf directory", new File(localUdfDir, default_binary_name).exists());
+ assertTrue("Source should exist in local udf directory", new File(localUdfDir, default_source_name).exists());
+ }
+
+ @Test
+ public void testDropFunction() throws Exception {
+ copyDefaultJarsToStagingArea();
+ test("create function using jar '%s'", default_binary_name);
+ test("select custom_lower('A') from (values(1))");
+
+ Path localUdfDirPath = Deencapsulation.getField(getDrillbitContext().getFunctionImplementationRegistry(), "localUdfDir");
+ File localUdfDir = new File(localUdfDirPath.toUri().getPath());
+
+ assertTrue("Binary should exist in local udf directory", new File(localUdfDir, default_binary_name).exists());
+ assertTrue("Source should exist in local udf directory", new File(localUdfDir, default_source_name).exists());
+
+ String summary = "The following UDFs in jar %s have been unregistered:\n" +
+ "[custom_lower(VARCHAR-REQUIRED)]";
+
+ testBuilder()
+ .sqlQuery("drop function using jar '%s'", default_binary_name)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(true, String.format(summary, default_binary_name))
+ .go();
+
+ try {
+ test("select custom_lower('A') from (values(1))");
+ } catch (UserRemoteException e){
+ assertThat(e.getMessage(), containsString("No match found for function signature custom_lower(<CHARACTER>)"));
+ }
+
+ RemoteFunctionRegistry remoteFunctionRegistry = getDrillbitContext().getRemoteFunctionRegistry();
+ assertEquals("Remote registry should be empty", remoteFunctionRegistry.getRegistry().getJarList().size(), 0);
+
+ FileSystem fs = remoteFunctionRegistry.getFs();
+ assertFalse("Binary should not be present in registry area",
+ fs.exists(new Path(remoteFunctionRegistry.getRegistryArea(), default_binary_name)));
+ assertFalse("Source should not be present in registry area",
+ fs.exists(new Path(remoteFunctionRegistry.getRegistryArea(), default_source_name)));
+
+ assertFalse("Binary should not be present in local udf directory",
+ new File(localUdfDir, default_binary_name).exists());
+ assertFalse("Source should not be present in local udf directory",
+ new File(localUdfDir, default_source_name).exists());
+ }
+
+ @Test
+ public void testReRegisterTheSameJarWithDifferentContent() throws Exception {
+ copyDefaultJarsToStagingArea();
+ test("create function using jar '%s'", default_binary_name);
+ testBuilder()
+ .sqlQuery("select custom_lower('A') as res from (values(1))")
+ .unOrdered()
+ .baselineColumns("res")
+ .baselineValues("a")
+ .go();
+ test("drop function using jar '%s'", default_binary_name);
+
+ Thread.sleep(1000);
+
+ Path src = new Path(jars.toURI().getPath(), "v2");
+ copyJarsToStagingArea(src, default_binary_name, default_source_name);
+ test("create function using jar '%s'", default_binary_name);
+ testBuilder()
+ .sqlQuery("select custom_lower('A') as res from (values(1))")
+ .unOrdered()
+ .baselineColumns("res")
+ .baselineValues("a_v2")
+ .go();
+ }
+
+ @Test
+ public void testDropAbsentJar() throws Exception {
+ String summary = "Jar %s is not registered in remote registry";
+
+ testBuilder()
+ .sqlQuery("drop function using jar '%s'", default_binary_name)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, String.format(summary, default_binary_name))
+ .go();
+ }
+
+ @Test
+ public void testRegistryAreaCleanUpOnFail() throws Exception {
+ final RemoteFunctionRegistry remoteFunctionRegistry = spyRemoteFunctionRegistry();
+ final FileSystem fs = remoteFunctionRegistry.getFs();
+ final String errorMessage = "Failure during remote registry update.";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ assertTrue("Binary should be present in registry area",
+ fs.exists(new Path(remoteFunctionRegistry.getRegistryArea(), default_binary_name)));
+ assertTrue("Source should be present in registry area",
+ fs.exists(new Path(remoteFunctionRegistry.getRegistryArea(), default_source_name)));
+ throw new RuntimeException(errorMessage);
+ }
+ }).when(remoteFunctionRegistry).updateRegistry(any(Registry.class), any(DataChangeVersion.class));
+
+ copyDefaultJarsToStagingArea();
+
+ testBuilder()
+ .sqlQuery("create function using jar '%s'", default_binary_name)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, errorMessage)
+ .go();
+
+ assertFalse("Registry area should be empty", fs.listFiles(remoteFunctionRegistry.getRegistryArea(), false).hasNext());
+ assertFalse("Temporary area should be empty", fs.listFiles(remoteFunctionRegistry.getTmpArea(), false).hasNext());
+
+ assertTrue("Binary should be present in staging area",
+ fs.exists(new Path(remoteFunctionRegistry.getStagingArea(), default_binary_name)));
+ assertTrue("Source should be present in staging area",
+ fs.exists(new Path(remoteFunctionRegistry.getStagingArea(), default_source_name)));
+ }
+
+ @Test
+ public void testConcurrentRegistrationOfTheSameJar() throws Exception {
+ RemoteFunctionRegistry remoteFunctionRegistry = spyRemoteFunctionRegistry();
+
+ final CountDownLatch latch1 = new CountDownLatch(1);
+ final CountDownLatch latch2 = new CountDownLatch(1);
+
+ doAnswer(new Answer<String>() {
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ String result = (String) invocation.callRealMethod();
+ latch2.countDown();
+ latch1.await();
+ return result;
+ }
+ })
+ .doCallRealMethod()
+ .doCallRealMethod()
+ .when(remoteFunctionRegistry).addToJars(anyString(), any(RemoteFunctionRegistry.Action.class));
+
+
+ final String query = String.format("create function using jar '%s'", default_binary_name);
+
+ Thread thread = new Thread(new SimpleQueryRunner(query));
+ thread.start();
+ latch2.await();
+
+ try {
+ String summary = "Jar with %s name is used. Action: REGISTRATION";
+
+ testBuilder()
+ .sqlQuery(query)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, String.format(summary, default_binary_name))
+ .go();
+
+ testBuilder()
+ .sqlQuery("drop function using jar '%s'", default_binary_name)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false, String.format(summary, default_binary_name))
+ .go();
+
+ } finally {
+ latch1.countDown();
+ thread.join();
+ }
+ }
+
+ @Test
+ public void testConcurrentRemoteRegistryUpdateWithDuplicates() throws Exception {
+ RemoteFunctionRegistry remoteFunctionRegistry = spyRemoteFunctionRegistry();
+
+ final CountDownLatch latch1 = new CountDownLatch(1);
+ final CountDownLatch latch2 = new CountDownLatch(1);
+ final CountDownLatch latch3 = new CountDownLatch(1);
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ latch3.countDown();
+ latch1.await();
+ invocation.callRealMethod();
+ latch2.countDown();
+ return null;
+ }
+ }).doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ latch1.countDown();
+ latch2.await();
+ invocation.callRealMethod();
+ return null;
+ }
+ })
+ .when(remoteFunctionRegistry).updateRegistry(any(Registry.class), any(DataChangeVersion.class));
+
+
+ final String jarName1 = default_binary_name;
+ final String jarName2 = "DrillUDF_Copy-1.0.jar";
+ final String query = "create function using jar '%s'";
+
+ copyDefaultJarsToStagingArea();
+ copyJarsToStagingArea(jarName2, JarUtil.getSourceName(jarName2));
+
+ Thread thread1 = new Thread(new TestBuilderRunner(
+ testBuilder()
+ .sqlQuery(query, jarName1)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(true,
+ String.format("The following UDFs in jar %s have been registered:\n" +
+ "[custom_lower(VARCHAR-REQUIRED)]", jarName1))
+ ));
+
+ Thread thread2 = new Thread(new TestBuilderRunner(
+ testBuilder()
+ .sqlQuery(query, jarName2)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(false,
+ String.format("Found duplicated function in %s: custom_lower(VARCHAR-REQUIRED)", jarName1))
+ ));
+
+ thread1.start();
+ latch3.await();
+ thread2.start();
+
+ thread1.join();
+ thread2.join();
+
+ DataChangeVersion version = new DataChangeVersion();
+ Registry registry = remoteFunctionRegistry.getRegistry(version);
+ assertEquals("Remote registry version should match", 2, version.getVersion());
+ List<Jar> jarList = registry.getJarList();
+ assertEquals("Only one jar should be registered", 1, jarList.size());
+ assertEquals("Jar name should match", jarName1, jarList.get(0).getName());
+
+ verify(remoteFunctionRegistry, times(2)).updateRegistry(any(Registry.class), any(DataChangeVersion.class));
+ }
+
+ @Test
+ public void testConcurrentRemoteRegistryUpdateForDifferentJars() throws Exception {
+ RemoteFunctionRegistry remoteFunctionRegistry = spyRemoteFunctionRegistry();
+ final CountDownLatch latch1 = new CountDownLatch(1);
+ final CountDownLatch latch2 = new CountDownLatch(2);
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ latch2.countDown();
+ latch1.await();
+ invocation.callRealMethod();
+ return null;
+ }
+ })
+ .when(remoteFunctionRegistry).updateRegistry(any(Registry.class), any(DataChangeVersion.class));
+
+ final String jarName1 = default_binary_name;
+ final String jarName2 = "DrillUDF-2.0.jar";
+ final String query = "create function using jar '%s'";
+
+ copyDefaultJarsToStagingArea();
+ copyJarsToStagingArea(jarName2, JarUtil.getSourceName(jarName2));
+
+
+ Thread thread1 = new Thread(new TestBuilderRunner(
+ testBuilder()
+ .sqlQuery(query, jarName1)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(true,
+ String.format("The following UDFs in jar %s have been registered:\n" +
+ "[custom_lower(VARCHAR-REQUIRED)]", jarName1))
+ ));
+
+
+ Thread thread2 = new Thread(new TestBuilderRunner(
+ testBuilder()
+ .sqlQuery(query, jarName2)
+ .unOrdered()
+ .baselineColumns("ok", "summary")
+ .baselineValues(true, String.format("The following UDFs in jar %s have been registered:\n" +
+ "[custom_upper(VARCHAR-REQUIRED)]", jarName2))
+ ));
+
+ thread1.start();
+ thread2.start();
+
+ latch2.await();
+ latch1.countDown();
+
+ thread1.join();
+ thread2.join();
+
+ DataChangeVersion version = new DataChangeVersion();
+ Registry registry = remoteFunctionRegistry.getRegistry(version);
+ assertEquals("Remote registry version should match", 3, version.getVersion());
+
+ List<Jar> actualJars = registry.getJarList();
+ List<String> expectedJars = Lists.newArrayList(jarName1, jarName2);
+
+ assertEquals("Only one jar should be registered", 2, actualJars.size());
+ for (Jar jar : actualJars) {
+ assertTrue("Jar should be present in remote function registry", expectedJars.contains(jar.getName()));
+ }
+
+ verify(remoteFunctionRegistry, times(3)).updateRegistry(any(Registry.class), any(DataChangeVersion.class));
+ }
+
+ @Test
+ public void testLazyInitConcurrent() throws Exception {
+ FunctionImplementationRegistry functionImplementationRegistry = spyFunctionImplementationRegistry();
+ copyDefaultJarsToStagingArea();
+ test("create function using jar '%s'", default_binary_name);
+
+ final CountDownLatch latch1 = new CountDownLatch(1);
+ final CountDownLatch latch2 = new CountDownLatch(1);
+
+ final String query = "select custom_lower('A') from (values(1))";
+
+ doAnswer(new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ latch1.await();
+ boolean result = (boolean) invocation.callRealMethod();
+ assertTrue("loadRemoteFunctions() should return true", result);
+ latch2.countDown();
+ return true;
+ }
+ })
+ .doAnswer(new Answer() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ latch1.countDown();
+ latch2.await();
+ boolean result = (boolean) invocation.callRealMethod();
+ assertTrue("loadRemoteFunctions() should return true", result);
+ return true;
+ }
+ })
+ .when(functionImplementationRegistry).loadRemoteFunctions(anyLong());
+
+ SimpleQueryRunner simpleQueryRunner = new SimpleQueryRunner(query);
+ Thread thread1 = new Thread(simpleQueryRunner);
+ Thread thread2 = new Thread(simpleQueryRunner);
+
+ thread1.start();
+ thread2.start();
+
+ thread1.join();
+ thread2.join();
+
+ verify(functionImplementationRegistry, times(2)).loadRemoteFunctions(anyLong());
+ LocalFunctionRegistry localFunctionRegistry = Deencapsulation.getField(functionImplementationRegistry, "localFunctionRegistry");
+ assertEquals("Local functionRegistry version should match", 2L, localFunctionRegistry.getVersion());
+ }
+
+ @Test
+ public void testLazyInitNoReload() throws Exception {
+ FunctionImplementationRegistry functionImplementationRegistry = spyFunctionImplementationRegistry();
+ copyDefaultJarsToStagingArea();
+ test("create function using jar '%s'", default_binary_name);
+
+ doAnswer(new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ boolean result = (boolean) invocation.callRealMethod();
+ assertTrue("loadRemoteFunctions() should return true", result);
+ return true;
+ }
+ })
+ .doAnswer(new Answer() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ boolean result = (boolean) invocation.callRealMethod();
+ assertFalse("loadRemoteFunctions() should return false", result);
+ return false;
+ }
+ })
+ .when(functionImplementationRegistry).loadRemoteFunctions(anyLong());
+
+ test("select custom_lower('A') from (values(1))");
+
+ try {
+ test("select unknown_lower('A') from (values(1))");
+ } catch (UserRemoteException e){
+ assertThat(e.getMessage(), containsString("No match found for function signature unknown_lower(<CHARACTER>)"));
+ }
+
+ verify(functionImplementationRegistry, times(2)).loadRemoteFunctions(anyLong());
+ LocalFunctionRegistry localFunctionRegistry = Deencapsulation.getField(functionImplementationRegistry, "localFunctionRegistry");
+ assertEquals("Local functionRegistry version should match", 2L, localFunctionRegistry.getVersion());
+ }
+
+ private void copyDefaultJarsToStagingArea() throws IOException {
+ copyJarsToStagingArea(new Path(jars.toURI()), default_binary_name, default_source_name);
+ }
+
+ private void copyJarsToStagingArea(String binaryName, String sourceName) throws IOException {
+ copyJarsToStagingArea(new Path(jars.toURI()), binaryName, sourceName);
+ }
+
+ private void copyJarsToStagingArea(Path src, String binaryName, String sourceName) throws IOException {
+ RemoteFunctionRegistry remoteFunctionRegistry = getDrillbitContext().getRemoteFunctionRegistry();
+ copyJar(remoteFunctionRegistry.getFs(), src, remoteFunctionRegistry.getStagingArea(), binaryName);
+ copyJar(remoteFunctionRegistry.getFs(), src, remoteFunctionRegistry.getStagingArea(), sourceName);
+ }
+
+ private void copyJar(FileSystem fs, Path src, Path dest, String name) throws IOException {
+ Path jarPath = new Path(src, name);
+ fs.copyFromLocalFile(jarPath, dest);
+ }
+
+ private RemoteFunctionRegistry spyRemoteFunctionRegistry() {
+ FunctionImplementationRegistry functionImplementationRegistry = getDrillbitContext().getFunctionImplementationRegistry();
+ RemoteFunctionRegistry remoteFunctionRegistry = functionImplementationRegistry.getRemoteFunctionRegistry();
+ RemoteFunctionRegistry spy = spy(remoteFunctionRegistry);
+ Deencapsulation.setField(functionImplementationRegistry, "remoteFunctionRegistry", spy);
+ return spy;
+ }
+
+ private FunctionImplementationRegistry spyFunctionImplementationRegistry() {
+ DrillbitContext drillbitContext = getDrillbitContext();
+ FunctionImplementationRegistry spy = spy(drillbitContext.getFunctionImplementationRegistry());
+ Deencapsulation.setField(drillbitContext, "functionRegistry", spy);
+ return spy;
+ }
+
+ private class SimpleQueryRunner implements Runnable {
+
+ private final String query;
+
+ SimpleQueryRunner(String query) {
+ this.query = query;
+ }
+
+ @Override
+ public void run() {
+ try {
+ test(query);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private class TestBuilderRunner implements Runnable {
+
+ private final TestBuilder testBuilder;
+
+ TestBuilderRunner(TestBuilder testBuilder) {
+ this.testBuilder = testBuilder;
+ }
+
+ @Override
+ public void run() {
+ try {
+ testBuilder.go();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/java/org/apache/drill/exec/ExecTest.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/ExecTest.java b/exec/java-exec/src/test/java/org/apache/drill/exec/ExecTest.java
index 2f26914..05f0a8d 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/ExecTest.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/ExecTest.java
@@ -18,7 +18,9 @@
package org.apache.drill.exec;
import com.codahale.metrics.MetricRegistry;
+import com.google.common.io.Files;
import mockit.NonStrictExpectations;
+import org.apache.commons.io.FileUtils;
import org.apache.drill.common.config.DrillConfig;
import org.apache.drill.common.scanner.ClassPathScanner;
import org.apache.drill.exec.compile.CodeCompilerTestFactory;
@@ -33,6 +35,8 @@ import org.apache.drill.test.DrillTest;
import org.junit.After;
import org.junit.BeforeClass;
+import java.io.File;
+
public class ExecTest extends DrillTest {
@@ -46,7 +50,6 @@ public class ExecTest extends DrillTest {
DrillMetrics.resetMetrics();
}
-
@BeforeClass
public static void setupOptionManager() throws Exception{
final LocalPersistentStoreProvider provider = new LocalPersistentStoreProvider(c);
@@ -55,6 +58,23 @@ public class ExecTest extends DrillTest {
optionManager.init();
}
+ /**
+ * Create a temp directory to store the given <i>dirName</i>.
+ * Directory will be deleted on exit.
+ * @param dirName directory name
+ * @return Full path including temp parent directory and given directory name.
+ */
+ public static String getTempDir(final String dirName) {
+ final File dir = Files.createTempDir();
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ FileUtils.deleteQuietly(dir);
+ }
+ });
+ return dir.getAbsolutePath() + File.separator + dirName;
+ }
+
protected void mockDrillbitContext(final DrillbitContext bitContext) throws Exception {
new NonStrictExpectations() {{
bitContext.getMetrics(); result = new MetricRegistry();
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/java/org/apache/drill/exec/coord/zk/TestZookeeperClient.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/coord/zk/TestZookeeperClient.java b/exec/java-exec/src/test/java/org/apache/drill/exec/coord/zk/TestZookeeperClient.java
index 3007566..ab886c4 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/coord/zk/TestZookeeperClient.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/coord/zk/TestZookeeperClient.java
@@ -37,6 +37,8 @@ import org.apache.curator.test.TestingServer;
import org.apache.curator.utils.EnsurePath;
import org.apache.drill.common.collections.ImmutableEntry;
import org.apache.drill.common.exceptions.DrillRuntimeException;
+import org.apache.drill.exec.exception.VersionMismatchException;
+import org.apache.drill.exec.store.sys.store.DataChangeVersion;
import org.apache.zookeeper.CreateMode;
import org.junit.After;
import org.junit.Assert;
@@ -44,6 +46,9 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
public class TestZookeeperClient {
private final static String root = "/test";
private final static String path = "test-key";
@@ -139,13 +144,13 @@ public class TestZookeeperClient {
.when(client.getCache().getCurrentData(abspath))
.thenReturn(null);
- Assert.assertEquals("get should return null", null, client.get(path));
+ assertEquals("get should return null", null, client.get(path));
Mockito
.when(client.getCache().getCurrentData(abspath))
.thenReturn(new ChildData(abspath, null, data));
- Assert.assertEquals("get should return data", data, client.get(path, false));
+ assertEquals("get should return data", data, client.get(path, false));
}
@Test
@@ -194,8 +199,46 @@ public class TestZookeeperClient {
// returned entry must contain the given relative path
final Map.Entry<String, byte[]> expected = new ImmutableEntry<>(path, data);
- Assert.assertEquals("entries do not match", expected, entries.next());
+ assertEquals("entries do not match", expected, entries.next());
+ }
+
+ @Test
+ public void testGetWithVersion() {
+ client.put(path, data);
+ DataChangeVersion version0 = new DataChangeVersion();
+ client.get(path, version0);
+ assertEquals("Versions should match", 0, version0.getVersion());
+ client.put(path, data);
+ DataChangeVersion version1 = new DataChangeVersion();
+ client.get(path, version1);
+ assertEquals("Versions should match", 1, version1.getVersion());
+ }
+
+ @Test
+ public void testPutWithMatchingVersion() {
+ client.put(path, data);
+ DataChangeVersion version = new DataChangeVersion();
+ client.get(path, version);
+ client.put(path, data, version);
+ }
+
+ @Test (expected = VersionMismatchException.class)
+ public void testPutWithNonMatchingVersion() {
+ client.put(path, data);
+ DataChangeVersion version = new DataChangeVersion();
+ version.setVersion(123);
+ client.put(path, data, version);
}
+ @Test
+ public void testPutIfAbsentWhenAbsent() {
+ assertNull(client.putIfAbsent(path, data));
+ }
+
+ @Test
+ public void testPutIfAbsentWhenPresent() {
+ client.putIfAbsent(path, data);
+ assertEquals("Data should match", new String(data), new String(client.putIfAbsent(path, "new_data".getBytes())));
+ }
}
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/java/org/apache/drill/exec/expr/fn/registry/FunctionRegistryHolderTest.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/expr/fn/registry/FunctionRegistryHolderTest.java b/exec/java-exec/src/test/java/org/apache/drill/exec/expr/fn/registry/FunctionRegistryHolderTest.java
new file mode 100644
index 0000000..61fa4e5
--- /dev/null
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/expr/fn/registry/FunctionRegistryHolderTest.java
@@ -0,0 +1,279 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.drill.exec.expr.fn.registry;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.drill.exec.expr.fn.DrillFuncHolder;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+public class FunctionRegistryHolderTest {
+
+ private static final String built_in = "built-in";
+ private static final String udf_jar = "DrillUDF-1.0.jar";
+
+ private static Map<String, List<FunctionHolder>> newJars;
+ private FunctionRegistryHolder registryHolder;
+
+ @BeforeClass
+ public static void init() {
+ newJars = Maps.newHashMap();
+ FunctionHolder lower = new FunctionHolder("lower", "lower(VARCHAR-REQUIRED)", mock(DrillFuncHolder.class));
+ FunctionHolder upper = new FunctionHolder("upper", "upper(VARCHAR-REQUIRED)", mock(DrillFuncHolder.class));
+ newJars.put(built_in, Lists.newArrayList(lower, upper));
+ FunctionHolder custom_lower = new FunctionHolder("custom_lower", "lower(VARCHAR-REQUIRED)", mock(DrillFuncHolder.class));
+ FunctionHolder custom_upper = new FunctionHolder("custom_upper", "custom_upper(VARCHAR-REQUIRED)", mock(DrillFuncHolder.class));
+ newJars.put(udf_jar, Lists.newArrayList(custom_lower, custom_upper));
+ }
+
+ @Before
+ public void setup() {
+ resetRegistry();
+ fillInRegistry();
+ }
+
+ @Test
+ public void testVersion() {
+ resetRegistry();
+ assertEquals("Initial version should be 0", 0, registryHolder.getVersion());
+ registryHolder.addJars(Maps.<String, List<FunctionHolder>>newHashMap());
+ assertEquals("Version should not change if no jars were added.", 0, registryHolder.getVersion());
+ registryHolder.removeJar("unknown.jar");
+ assertEquals("Version should not change if no jars were removed.", 0, registryHolder.getVersion());
+ fillInRegistry();
+ assertEquals("Version should have incremented by 1", 1, registryHolder.getVersion());
+ registryHolder.removeJar(built_in);
+ assertEquals("Version should have incremented by 1", 2, registryHolder.getVersion());
+ fillInRegistry();
+ assertEquals("Version should have incremented by 1", 3, registryHolder.getVersion());
+ fillInRegistry();
+ assertEquals("Version should have incremented by 1", 4, registryHolder.getVersion());
+ }
+
+ @Test
+ public void testAddJars() {
+ resetRegistry();
+ int functionsSize = 0;
+ List<String> jars = Lists.newArrayList();
+ ListMultimap<String, DrillFuncHolder> functionsWithHolders = ArrayListMultimap.create();
+ ListMultimap<String, String> functionsWithSignatures = ArrayListMultimap.create();
+ for (Map.Entry<String, List<FunctionHolder>> jar : newJars.entrySet()) {
+ jars.add(jar.getKey());
+ for (FunctionHolder functionHolder : jar.getValue()) {
+ functionsWithHolders.put(functionHolder.getName(), functionHolder.getHolder());
+ functionsWithSignatures.put(functionHolder.getName(), functionHolder.getSignature());
+ functionsSize++;
+ }
+ }
+
+ registryHolder.addJars(newJars);
+ assertEquals("Version number should match", 1, registryHolder.getVersion());
+ compareTwoLists(jars, registryHolder.getAllJarNames());
+ assertEquals(functionsSize, registryHolder.functionsSize());
+ compareListMultimaps(functionsWithHolders, registryHolder.getAllFunctionsWithHolders());
+ compareListMultimaps(functionsWithSignatures, registryHolder.getAllFunctionsWithSignatures());
+ }
+
+ @Test
+ public void testAddTheSameJars() {
+ resetRegistry();
+ int functionsSize = 0;
+ List<String> jars = Lists.newArrayList();
+ ListMultimap<String, DrillFuncHolder> functionsWithHolders = ArrayListMultimap.create();
+ ListMultimap<String, String> functionsWithSignatures = ArrayListMultimap.create();
+ for (Map.Entry<String, List<FunctionHolder>> jar : newJars.entrySet()) {
+ jars.add(jar.getKey());
+ for (FunctionHolder functionHolder : jar.getValue()) {
+ functionsWithHolders.put(functionHolder.getName(), functionHolder.getHolder());
+ functionsWithSignatures.put(functionHolder.getName(), functionHolder.getSignature());
+ functionsSize++;
+ }
+ }
+ registryHolder.addJars(newJars);
+ assertEquals("Version number should match", 1, registryHolder.getVersion());
+ compareTwoLists(jars, registryHolder.getAllJarNames());
+ assertEquals(functionsSize, registryHolder.functionsSize());
+ compareListMultimaps(functionsWithHolders, registryHolder.getAllFunctionsWithHolders());
+ compareListMultimaps(functionsWithSignatures, registryHolder.getAllFunctionsWithSignatures());
+
+ // adding the same jars should not cause adding duplicates, should override existing jars only
+ registryHolder.addJars(newJars);
+ assertEquals("Version number should match", 2, registryHolder.getVersion());
+ compareTwoLists(jars, registryHolder.getAllJarNames());
+ assertEquals(functionsSize, registryHolder.functionsSize());
+ compareListMultimaps(functionsWithHolders, registryHolder.getAllFunctionsWithHolders());
+ compareListMultimaps(functionsWithSignatures, registryHolder.getAllFunctionsWithSignatures());
+ }
+
+ @Test
+ public void testRemoveJar() {
+ registryHolder.removeJar(built_in);
+ assertFalse("Jar should be absent", registryHolder.containsJar(built_in));
+ assertTrue("Jar should be present", registryHolder.containsJar(udf_jar));
+ assertEquals("Functions size should match", newJars.get(udf_jar).size(), registryHolder.functionsSize());
+ }
+
+ @Test
+ public void testGetAllJarNames() {
+ ArrayList<String> expectedResult = Lists.newArrayList(newJars.keySet());
+ compareTwoLists(expectedResult, registryHolder.getAllJarNames());
+ }
+
+ @Test
+ public void testGetFunctionNamesByJar() {
+ ArrayList<String> expectedResult = Lists.newArrayList();
+ for (FunctionHolder functionHolder : newJars.get(built_in)) {
+ expectedResult.add(functionHolder.getName());
+ }
+ compareTwoLists(expectedResult, registryHolder.getFunctionNamesByJar(built_in));
+ }
+
+ @Test
+ public void testGetAllFunctionsWithHoldersWithVersion() {
+ ListMultimap<String, DrillFuncHolder> expectedResult = ArrayListMultimap.create();
+ for (List<FunctionHolder> functionHolders : newJars.values()) {
+ for(FunctionHolder functionHolder : functionHolders) {
+ expectedResult.put(functionHolder.getName(), functionHolder.getHolder());
+ }
+ }
+ AtomicLong version = new AtomicLong();
+ compareListMultimaps(expectedResult, registryHolder.getAllFunctionsWithHolders(version));
+ assertEquals("Version number should match", version.get(), registryHolder.getVersion());
+ }
+
+ @Test
+ public void testGetAllFunctionsWithHolders() {
+ ListMultimap<String, DrillFuncHolder> expectedResult = ArrayListMultimap.create();
+ for (List<FunctionHolder> functionHolders : newJars.values()) {
+ for(FunctionHolder functionHolder : functionHolders) {
+ expectedResult.put(functionHolder.getName(), functionHolder.getHolder());
+ }
+ }
+ compareListMultimaps(expectedResult, registryHolder.getAllFunctionsWithHolders());
+ }
+
+ @Test
+ public void testGetAllFunctionsWithSignatures() {
+ ListMultimap<String, String> expectedResult = ArrayListMultimap.create();
+ for (List<FunctionHolder> functionHolders : newJars.values()) {
+ for(FunctionHolder functionHolder : functionHolders) {
+ expectedResult.put(functionHolder.getName(), functionHolder.getSignature());
+ }
+ }
+ compareListMultimaps(expectedResult, registryHolder.getAllFunctionsWithSignatures());
+ }
+
+ @Test
+ public void testGetHoldersByFunctionNameWithVersion() {
+ List<DrillFuncHolder> expectedResult = Lists.newArrayList();
+ for (List<FunctionHolder> functionHolders : newJars.values()) {
+ for (FunctionHolder functionHolder : functionHolders) {
+ if ("lower".equals(functionHolder.getName())) {
+ expectedResult.add(functionHolder.getHolder()) ;
+ }
+ }
+ }
+ assertFalse(expectedResult.isEmpty());
+ AtomicLong version = new AtomicLong();
+ compareTwoLists(expectedResult, registryHolder.getHoldersByFunctionName("lower", version));
+ assertEquals("Version number should match", version.get(), registryHolder.getVersion());
+ }
+
+ @Test
+ public void testGetHoldersByFunctionName() {
+ List<DrillFuncHolder> expectedResult = Lists.newArrayList();
+ for (List<FunctionHolder> functionHolders : newJars.values()) {
+ for (FunctionHolder functionHolder : functionHolders) {
+ if ("lower".equals(functionHolder.getName())) {
+ expectedResult.add(functionHolder.getHolder()) ;
+ }
+ }
+ }
+ assertFalse(expectedResult.isEmpty());
+ compareTwoLists(expectedResult, registryHolder.getHoldersByFunctionName("lower"));
+ }
+
+ @Test
+ public void testContainsJar() {
+ assertTrue("Jar should be present in registry holder", registryHolder.containsJar(built_in));
+ assertFalse("Jar should be absent in registry holder", registryHolder.containsJar("unknown.jar"));
+ }
+
+ @Test
+ public void testFunctionsSize() {
+ int count = 0;
+ for (List<FunctionHolder> functionHolders : newJars.values()) {
+ count += functionHolders.size();
+ }
+ assertEquals("Functions size should match", count, registryHolder.functionsSize());
+ }
+
+ @Test
+ public void testJarNameByFunctionSignature() {
+ FunctionHolder functionHolder = newJars.get(built_in).get(0);
+ assertEquals("Jar name should match",
+ built_in, registryHolder.getJarNameByFunctionSignature(functionHolder.getName(), functionHolder.getSignature()));
+ assertNull("Jar name should be null",
+ registryHolder.getJarNameByFunctionSignature("unknown_function", "unknown_function(unknown-input)"));
+ }
+
+ private void resetRegistry() {
+ registryHolder = new FunctionRegistryHolder();
+ }
+
+ private void fillInRegistry() {
+ registryHolder.addJars(newJars);
+ }
+
+ private <T> void compareListMultimaps(ListMultimap<String, T> lm1, ListMultimap<String, T> lm2) {
+ Map<String, Collection<T>> m1 = lm1.asMap();
+ Map<String, Collection<T>> m2 = lm2.asMap();
+ assertEquals("Multimaps size should match", m1.size(), m2.size());
+ for (Map.Entry<String, Collection<T>> entry : m1.entrySet()) {
+ try {
+ compareTwoLists(Lists.newArrayList(entry.getValue()), Lists.newArrayList(m2.get(entry.getKey())));
+ } catch (AssertionError e) {
+ throw new AssertionError("Multimaps values should match", e);
+ }
+ }
+ }
+
+ private <T> void compareTwoLists(List<T> l1, List<T> l2) {
+ assertEquals("Lists size should match", l1.size(), l2.size());
+ for (T item : l1) {
+ assertTrue("Two lists should have the same values", l2.contains(item));
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestSimpleFunctions.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestSimpleFunctions.java b/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestSimpleFunctions.java
index 64495db..ede30e6 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestSimpleFunctions.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestSimpleFunctions.java
@@ -65,17 +65,17 @@ public class TestSimpleFunctions extends ExecTest {
private final DrillConfig c = DrillConfig.create();
@Test
- public void testHashFunctionResolution(@Injectable DrillConfig config) throws JClassAlreadyExistsException, IOException {
- final FunctionImplementationRegistry registry = new FunctionImplementationRegistry(config);
+ public void testHashFunctionResolution() throws JClassAlreadyExistsException, IOException {
+ final FunctionImplementationRegistry registry = new FunctionImplementationRegistry(c);
// test required vs nullable Int input
- resolveHash(config,
+ resolveHash(c,
new TypedNullConstant(Types.optional(TypeProtos.MinorType.INT)),
Types.optional(TypeProtos.MinorType.INT),
Types.required(TypeProtos.MinorType.INT),
TypeProtos.DataMode.OPTIONAL,
registry);
- resolveHash(config,
+ resolveHash(c,
new ValueExpressions.IntExpression(1, ExpressionPosition.UNKNOWN),
Types.required(TypeProtos.MinorType.INT),
Types.required(TypeProtos.MinorType.INT),
@@ -83,14 +83,14 @@ public class TestSimpleFunctions extends ExecTest {
registry);
// test required vs nullable float input
- resolveHash(config,
+ resolveHash(c,
new TypedNullConstant(Types.optional(TypeProtos.MinorType.FLOAT4)),
Types.optional(TypeProtos.MinorType.FLOAT4),
Types.required(TypeProtos.MinorType.FLOAT4),
TypeProtos.DataMode.OPTIONAL,
registry);
- resolveHash(config,
+ resolveHash(c,
new ValueExpressions.FloatExpression(5.0f, ExpressionPosition.UNKNOWN),
Types.required(TypeProtos.MinorType.FLOAT4),
Types.required(TypeProtos.MinorType.FLOAT4),
@@ -98,14 +98,14 @@ public class TestSimpleFunctions extends ExecTest {
registry);
// test required vs nullable long input
- resolveHash(config,
+ resolveHash(c,
new TypedNullConstant(Types.optional(TypeProtos.MinorType.BIGINT)),
Types.optional(TypeProtos.MinorType.BIGINT),
Types.required(TypeProtos.MinorType.BIGINT),
TypeProtos.DataMode.OPTIONAL,
registry);
- resolveHash(config,
+ resolveHash(c,
new ValueExpressions.LongExpression(100L, ExpressionPosition.UNKNOWN),
Types.required(TypeProtos.MinorType.BIGINT),
Types.required(TypeProtos.MinorType.BIGINT),
@@ -113,14 +113,14 @@ public class TestSimpleFunctions extends ExecTest {
registry);
// test required vs nullable double input
- resolveHash(config,
+ resolveHash(c,
new TypedNullConstant(Types.optional(TypeProtos.MinorType.FLOAT8)),
Types.optional(TypeProtos.MinorType.FLOAT8),
Types.required(TypeProtos.MinorType.FLOAT8),
TypeProtos.DataMode.OPTIONAL,
registry);
- resolveHash(config,
+ resolveHash(c,
new ValueExpressions.DoubleExpression(100.0, ExpressionPosition.UNKNOWN),
Types.required(TypeProtos.MinorType.FLOAT8),
Types.required(TypeProtos.MinorType.FLOAT8),
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/DrillUDF-1.0-sources.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/DrillUDF-1.0-sources.jar b/exec/java-exec/src/test/resources/jars/DrillUDF-1.0-sources.jar
new file mode 100644
index 0000000..b5965c9
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/DrillUDF-1.0-sources.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/DrillUDF-1.0.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/DrillUDF-1.0.jar b/exec/java-exec/src/test/resources/jars/DrillUDF-1.0.jar
new file mode 100644
index 0000000..7cd2eeb
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/DrillUDF-1.0.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/DrillUDF-2.0-sources.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/DrillUDF-2.0-sources.jar b/exec/java-exec/src/test/resources/jars/DrillUDF-2.0-sources.jar
new file mode 100644
index 0000000..1c8308c
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/DrillUDF-2.0-sources.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/DrillUDF-2.0.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/DrillUDF-2.0.jar b/exec/java-exec/src/test/resources/jars/DrillUDF-2.0.jar
new file mode 100644
index 0000000..3522c1e
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/DrillUDF-2.0.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/DrillUDF_Copy-1.0-sources.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/DrillUDF_Copy-1.0-sources.jar b/exec/java-exec/src/test/resources/jars/DrillUDF_Copy-1.0-sources.jar
new file mode 100644
index 0000000..fa449e2
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/DrillUDF_Copy-1.0-sources.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/DrillUDF_Copy-1.0.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/DrillUDF_Copy-1.0.jar b/exec/java-exec/src/test/resources/jars/DrillUDF_Copy-1.0.jar
new file mode 100644
index 0000000..8945fe7
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/DrillUDF_Copy-1.0.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/DrillUDF_DupFunc-1.0-sources.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/DrillUDF_DupFunc-1.0-sources.jar b/exec/java-exec/src/test/resources/jars/DrillUDF_DupFunc-1.0-sources.jar
new file mode 100644
index 0000000..b19ade6
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/DrillUDF_DupFunc-1.0-sources.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/DrillUDF_DupFunc-1.0.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/DrillUDF_DupFunc-1.0.jar b/exec/java-exec/src/test/resources/jars/DrillUDF_DupFunc-1.0.jar
new file mode 100644
index 0000000..56a649c
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/DrillUDF_DupFunc-1.0.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/DrillUDF_Empty-1.0-sources.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/DrillUDF_Empty-1.0-sources.jar b/exec/java-exec/src/test/resources/jars/DrillUDF_Empty-1.0-sources.jar
new file mode 100644
index 0000000..2a82dc9
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/DrillUDF_Empty-1.0-sources.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/DrillUDF_Empty-1.0.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/DrillUDF_Empty-1.0.jar b/exec/java-exec/src/test/resources/jars/DrillUDF_Empty-1.0.jar
new file mode 100644
index 0000000..11ed28b
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/DrillUDF_Empty-1.0.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/DrillUDF_NoMarkerFile-1.0-sources.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/DrillUDF_NoMarkerFile-1.0-sources.jar b/exec/java-exec/src/test/resources/jars/DrillUDF_NoMarkerFile-1.0-sources.jar
new file mode 100644
index 0000000..dbc97dd
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/DrillUDF_NoMarkerFile-1.0-sources.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/DrillUDF_NoMarkerFile-1.0.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/DrillUDF_NoMarkerFile-1.0.jar b/exec/java-exec/src/test/resources/jars/DrillUDF_NoMarkerFile-1.0.jar
new file mode 100644
index 0000000..cba65da
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/DrillUDF_NoMarkerFile-1.0.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/v2/DrillUDF-1.0-sources.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/v2/DrillUDF-1.0-sources.jar b/exec/java-exec/src/test/resources/jars/v2/DrillUDF-1.0-sources.jar
new file mode 100644
index 0000000..583b1c4
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/v2/DrillUDF-1.0-sources.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/java-exec/src/test/resources/jars/v2/DrillUDF-1.0.jar
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/resources/jars/v2/DrillUDF-1.0.jar b/exec/java-exec/src/test/resources/jars/v2/DrillUDF-1.0.jar
new file mode 100644
index 0000000..42df4a4
Binary files /dev/null and b/exec/java-exec/src/test/resources/jars/v2/DrillUDF-1.0.jar differ
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/exec/jdbc-all/src/test/java/org/apache/drill/jdbc/ITTestShadedJar.java
----------------------------------------------------------------------
diff --git a/exec/jdbc-all/src/test/java/org/apache/drill/jdbc/ITTestShadedJar.java b/exec/jdbc-all/src/test/java/org/apache/drill/jdbc/ITTestShadedJar.java
index 7258661..f64b5b7 100644
--- a/exec/jdbc-all/src/test/java/org/apache/drill/jdbc/ITTestShadedJar.java
+++ b/exec/jdbc-all/src/test/java/org/apache/drill/jdbc/ITTestShadedJar.java
@@ -17,6 +17,7 @@
*/
package org.apache.drill.jdbc;
+import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
@@ -27,8 +28,10 @@ import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.UUID;
import java.util.Vector;
+import org.apache.commons.io.FileUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -47,6 +50,22 @@ public class ITTestShadedJar {
}
+ static {
+ String dirConfDir = "DRILL_CONF_DIR";
+ if (System.getProperty(dirConfDir) == null) {
+ final File condDir = new File(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
+ condDir.mkdirs();
+ condDir.deleteOnExit();
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ FileUtils.deleteQuietly(condDir);
+ }
+ });
+ System.setProperty(dirConfDir, condDir.getAbsolutePath());
+ }
+ }
+
@Test
public void executeJdbcAllQuery() throws Exception {
http://git-wip-us.apache.org/repos/asf/drill/blob/89f2633f/protocol/src/main/java/org/apache/drill/exec/proto/SchemaUserBitShared.java
----------------------------------------------------------------------
diff --git a/protocol/src/main/java/org/apache/drill/exec/proto/SchemaUserBitShared.java b/protocol/src/main/java/org/apache/drill/exec/proto/SchemaUserBitShared.java
index 58efae3..037484d 100644
--- a/protocol/src/main/java/org/apache/drill/exec/proto/SchemaUserBitShared.java
+++ b/protocol/src/main/java/org/apache/drill/exec/proto/SchemaUserBitShared.java
@@ -2678,4 +2678,235 @@ public final class SchemaUserBitShared
}
}
+ public static final class Registry
+ {
+ public static final org.apache.drill.exec.proto.SchemaUserBitShared.Registry.MessageSchema WRITE =
+ new org.apache.drill.exec.proto.SchemaUserBitShared.Registry.MessageSchema();
+ public static final org.apache.drill.exec.proto.SchemaUserBitShared.Registry.BuilderSchema MERGE =
+ new org.apache.drill.exec.proto.SchemaUserBitShared.Registry.BuilderSchema();
+
+ public static class MessageSchema implements com.dyuproject.protostuff.Schema<org.apache.drill.exec.proto.UserBitShared.Registry>
+ {
+ public void writeTo(com.dyuproject.protostuff.Output output, org.apache.drill.exec.proto.UserBitShared.Registry message) throws java.io.IOException
+ {
+ for(org.apache.drill.exec.proto.UserBitShared.Jar jar : message.getJarList())
+ output.writeObject(1, jar, org.apache.drill.exec.proto.SchemaUserBitShared.Jar.WRITE, true);
+
+ }
+ public boolean isInitialized(org.apache.drill.exec.proto.UserBitShared.Registry message)
+ {
+ return message.isInitialized();
+ }
+ public java.lang.String getFieldName(int number)
+ {
+ return org.apache.drill.exec.proto.SchemaUserBitShared.Registry.getFieldName(number);
+ }
+ public int getFieldNumber(java.lang.String name)
+ {
+ return org.apache.drill.exec.proto.SchemaUserBitShared.Registry.getFieldNumber(name);
+ }
+ public java.lang.Class<org.apache.drill.exec.proto.UserBitShared.Registry> typeClass()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Registry.class;
+ }
+ public java.lang.String messageName()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Registry.class.getSimpleName();
+ }
+ public java.lang.String messageFullName()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Registry.class.getName();
+ }
+ //unused
+ public void mergeFrom(com.dyuproject.protostuff.Input input, org.apache.drill.exec.proto.UserBitShared.Registry message) throws java.io.IOException {}
+ public org.apache.drill.exec.proto.UserBitShared.Registry newMessage() { return null; }
+ }
+ public static class BuilderSchema implements com.dyuproject.protostuff.Schema<org.apache.drill.exec.proto.UserBitShared.Registry.Builder>
+ {
+ public void mergeFrom(com.dyuproject.protostuff.Input input, org.apache.drill.exec.proto.UserBitShared.Registry.Builder builder) throws java.io.IOException
+ {
+ for(int number = input.readFieldNumber(this);; number = input.readFieldNumber(this))
+ {
+ switch(number)
+ {
+ case 0:
+ return;
+ case 1:
+ builder.addJar(input.mergeObject(org.apache.drill.exec.proto.UserBitShared.Jar.newBuilder(), org.apache.drill.exec.proto.SchemaUserBitShared.Jar.MERGE));
+
+ break;
+ default:
+ input.handleUnknownField(number, this);
+ }
+ }
+ }
+ public boolean isInitialized(org.apache.drill.exec.proto.UserBitShared.Registry.Builder builder)
+ {
+ return builder.isInitialized();
+ }
+ public org.apache.drill.exec.proto.UserBitShared.Registry.Builder newMessage()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Registry.newBuilder();
+ }
+ public java.lang.String getFieldName(int number)
+ {
+ return org.apache.drill.exec.proto.SchemaUserBitShared.Registry.getFieldName(number);
+ }
+ public int getFieldNumber(java.lang.String name)
+ {
+ return org.apache.drill.exec.proto.SchemaUserBitShared.Registry.getFieldNumber(name);
+ }
+ public java.lang.Class<org.apache.drill.exec.proto.UserBitShared.Registry.Builder> typeClass()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Registry.Builder.class;
+ }
+ public java.lang.String messageName()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Registry.class.getSimpleName();
+ }
+ public java.lang.String messageFullName()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Registry.class.getName();
+ }
+ //unused
+ public void writeTo(com.dyuproject.protostuff.Output output, org.apache.drill.exec.proto.UserBitShared.Registry.Builder builder) throws java.io.IOException {}
+ }
+ public static java.lang.String getFieldName(int number)
+ {
+ switch(number)
+ {
+ case 1: return "jar";
+ default: return null;
+ }
+ }
+ public static int getFieldNumber(java.lang.String name)
+ {
+ java.lang.Integer number = fieldMap.get(name);
+ return number == null ? 0 : number.intValue();
+ }
+ private static final java.util.HashMap<java.lang.String,java.lang.Integer> fieldMap = new java.util.HashMap<java.lang.String,java.lang.Integer>();
+ static
+ {
+ fieldMap.put("jar", 1);
+ }
+ }
+
+ public static final class Jar
+ {
+ public static final org.apache.drill.exec.proto.SchemaUserBitShared.Jar.MessageSchema WRITE =
+ new org.apache.drill.exec.proto.SchemaUserBitShared.Jar.MessageSchema();
+ public static final org.apache.drill.exec.proto.SchemaUserBitShared.Jar.BuilderSchema MERGE =
+ new org.apache.drill.exec.proto.SchemaUserBitShared.Jar.BuilderSchema();
+
+ public static class MessageSchema implements com.dyuproject.protostuff.Schema<org.apache.drill.exec.proto.UserBitShared.Jar>
+ {
+ public void writeTo(com.dyuproject.protostuff.Output output, org.apache.drill.exec.proto.UserBitShared.Jar message) throws java.io.IOException
+ {
+ if(message.hasName())
+ output.writeString(1, message.getName(), false);
+ for(String functionSignature : message.getFunctionSignatureList())
+ output.writeString(2, functionSignature, true);
+ }
+ public boolean isInitialized(org.apache.drill.exec.proto.UserBitShared.Jar message)
+ {
+ return message.isInitialized();
+ }
+ public java.lang.String getFieldName(int number)
+ {
+ return org.apache.drill.exec.proto.SchemaUserBitShared.Jar.getFieldName(number);
+ }
+ public int getFieldNumber(java.lang.String name)
+ {
+ return org.apache.drill.exec.proto.SchemaUserBitShared.Jar.getFieldNumber(name);
+ }
+ public java.lang.Class<org.apache.drill.exec.proto.UserBitShared.Jar> typeClass()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Jar.class;
+ }
+ public java.lang.String messageName()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Jar.class.getSimpleName();
+ }
+ public java.lang.String messageFullName()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Jar.class.getName();
+ }
+ //unused
+ public void mergeFrom(com.dyuproject.protostuff.Input input, org.apache.drill.exec.proto.UserBitShared.Jar message) throws java.io.IOException {}
+ public org.apache.drill.exec.proto.UserBitShared.Jar newMessage() { return null; }
+ }
+ public static class BuilderSchema implements com.dyuproject.protostuff.Schema<org.apache.drill.exec.proto.UserBitShared.Jar.Builder>
+ {
+ public void mergeFrom(com.dyuproject.protostuff.Input input, org.apache.drill.exec.proto.UserBitShared.Jar.Builder builder) throws java.io.IOException
+ {
+ for(int number = input.readFieldNumber(this);; number = input.readFieldNumber(this))
+ {
+ switch(number)
+ {
+ case 0:
+ return;
+ case 1:
+ builder.setName(input.readString());
+ break;
+ case 2:
+ builder.addFunctionSignature(input.readString());
+ break;
+ default:
+ input.handleUnknownField(number, this);
+ }
+ }
+ }
+ public boolean isInitialized(org.apache.drill.exec.proto.UserBitShared.Jar.Builder builder)
+ {
+ return builder.isInitialized();
+ }
+ public org.apache.drill.exec.proto.UserBitShared.Jar.Builder newMessage()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Jar.newBuilder();
+ }
+ public java.lang.String getFieldName(int number)
+ {
+ return org.apache.drill.exec.proto.SchemaUserBitShared.Jar.getFieldName(number);
+ }
+ public int getFieldNumber(java.lang.String name)
+ {
+ return org.apache.drill.exec.proto.SchemaUserBitShared.Jar.getFieldNumber(name);
+ }
+ public java.lang.Class<org.apache.drill.exec.proto.UserBitShared.Jar.Builder> typeClass()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Jar.Builder.class;
+ }
+ public java.lang.String messageName()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Jar.class.getSimpleName();
+ }
+ public java.lang.String messageFullName()
+ {
+ return org.apache.drill.exec.proto.UserBitShared.Jar.class.getName();
+ }
+ //unused
+ public void writeTo(com.dyuproject.protostuff.Output output, org.apache.drill.exec.proto.UserBitShared.Jar.Builder builder) throws java.io.IOException {}
+ }
+ public static java.lang.String getFieldName(int number)
+ {
+ switch(number)
+ {
+ case 1: return "name";
+ case 2: return "functionSignature";
+ default: return null;
+ }
+ }
+ public static int getFieldNumber(java.lang.String name)
+ {
+ java.lang.Integer number = fieldMap.get(name);
+ return number == null ? 0 : number.intValue();
+ }
+ private static final java.util.HashMap<java.lang.String,java.lang.Integer> fieldMap = new java.util.HashMap<java.lang.String,java.lang.Integer>();
+ static
+ {
+ fieldMap.put("name", 1);
+ fieldMap.put("functionSignature", 2);
+ }
+ }
+
}