This is an automated email from the ASF dual-hosted git repository.

jackie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 5390698  Fix the failure caused by Reflections in FunctionRegistry 
(#5531)
5390698 is described below

commit 53906986e376e7b454f1d23599794b07683293b0
Author: Xiaotian (Jackie) Jiang <17555551+jackie-ji...@users.noreply.github.com>
AuthorDate: Tue Jun 9 19:07:53 2020 -0700

    Fix the failure caused by Reflections in FunctionRegistry (#5531)
    
    The new FunctionRegistry change of using reflection to plug in methods 
(introduced in #5440) is causing failure in query compilation.
    The problem is caused by Reflections library not being thread-safe when 
multiple threads are accessing the same jar file.
    We have multiple libraries (jersey, swagger) using reflection to load 
classes. The FunctionRegistry uses reflection in the static block, which 
happens when the class is loaded. If the first query arrives (first usage of 
FunctionRegistry which runs the static block) during the setup of the jersey 
server, Reflections will throw exception.
    Using reflection in the static block can also cause the first query to 
block on reflection scanning the classes, which can potentially take seconds.
    Read more about the thread-safety issue here: ronmamo/reflections#81
    Users are reporting the same issue even with the latest Reflections version 
0.9.12.
    A common solution is to downgrade Reflections to 0.9.9, but we have other 
dependencies rely on 0.9.11, so downgrading might cause other issues.
    
    The solution here is to introduce a no-op init() method to trigger the 
static block so that we can control when to scan the files to avoid the 
thread-safety issue as well as blocking the first query.
    Another benefit of using an init() method is that the exception won't be 
swallowed (the exception in static block will be swallowed and query engine 
will start getting ClassNotFoundException)
    
    This PR also introduces a convention for the plugin methods using 
reflection, where the class must includes ".function." in its class path.
    This can significantly reduce the time of class scanning (reduced from 4 
seconds to 200 ms locally)
---
 .../broker/broker/helix/HelixBrokerStarter.java    |  8 +-
 .../pinot/common/function/FunctionRegistry.java    | 94 ++++++++++++----------
 .../apache/pinot/sql/parsers/CalciteSqlParser.java | 31 +++----
 .../apache/pinot/controller/ControllerStarter.java |  4 +
 .../data/function/InbuiltFunctionEvaluator.java    |  8 +-
 .../function/TransformFunctionFactory.java         | 40 ++++-----
 .../function/InbuiltFunctionEvaluatorTest.java     | 42 +++++-----
 .../core/data/function/InbuiltFunctionsTest.java   | 15 ++--
 .../pinot/server/starter/ServerInstance.java       |  3 +
 9 files changed, 127 insertions(+), 118 deletions(-)

diff --git 
a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/HelixBrokerStarter.java
 
b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/HelixBrokerStarter.java
index 50acf54..96681b2 100644
--- 
a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/HelixBrokerStarter.java
+++ 
b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/HelixBrokerStarter.java
@@ -49,6 +49,7 @@ import 
org.apache.pinot.broker.requesthandler.BrokerRequestHandler;
 import 
org.apache.pinot.broker.requesthandler.SingleConnectionBrokerRequestHandler;
 import org.apache.pinot.broker.routing.RoutingManager;
 import org.apache.pinot.common.Utils;
+import org.apache.pinot.common.function.FunctionRegistry;
 import org.apache.pinot.common.metadata.ZKMetadataProvider;
 import org.apache.pinot.common.metrics.BrokerMeter;
 import org.apache.pinot.common.metrics.BrokerMetrics;
@@ -210,6 +211,8 @@ public class HelixBrokerStarter implements ServiceStartable 
{
     HelixExternalViewBasedQueryQuotaManager queryQuotaManager =
         new HelixExternalViewBasedQueryQuotaManager(_brokerMetrics);
     queryQuotaManager.init(_spectatorHelixManager);
+    // Initialize FunctionRegistry before starting the broker request handler
+    FunctionRegistry.init();
     _brokerRequestHandler =
         new SingleConnectionBrokerRequestHandler(_brokerConf, _routingManager, 
_accessControlFactory, queryQuotaManager,
             _brokerMetrics, _propertyStore);
@@ -291,8 +294,8 @@ public class HelixBrokerStarter implements ServiceStartable 
{
         Broker.DEFAULT_BROKER_MIN_RESOURCE_PERCENT_FOR_START);
 
     LOGGER.info("Registering service status handler");
-    ServiceStatus.setServiceStatusCallback(_brokerId, new 
ServiceStatus.MultipleCallbackServiceStatusCallback(ImmutableList
-        .of(new 
ServiceStatus.IdealStateAndCurrentStateMatchServiceStatusCallback(_participantHelixManager,
+    ServiceStatus.setServiceStatusCallback(_brokerId, new 
ServiceStatus.MultipleCallbackServiceStatusCallback(
+        ImmutableList.of(new 
ServiceStatus.IdealStateAndCurrentStateMatchServiceStatusCallback(_participantHelixManager,
                 _clusterName, _brokerId, resourcesToMonitor, 
minResourcePercentForStartup),
             new 
ServiceStatus.IdealStateAndExternalViewMatchServiceStatusCallback(_participantHelixManager,
                 _clusterName, _brokerId, resourcesToMonitor, 
minResourcePercentForStartup))));
@@ -345,7 +348,6 @@ public class HelixBrokerStarter implements ServiceStartable 
{
     LOGGER.info("Shutdown Broker Metrics Registry");
     _metricsRegistry.shutdown();
     LOGGER.info("Finish shutting down Pinot broker for {}", _brokerId);
-
   }
 
   public HelixManager getSpectatorHelixManager() {
diff --git 
a/pinot-common/src/main/java/org/apache/pinot/common/function/FunctionRegistry.java
 
b/pinot-common/src/main/java/org/apache/pinot/common/function/FunctionRegistry.java
index 479a67d..c8b26cf 100644
--- 
a/pinot-common/src/main/java/org/apache/pinot/common/function/FunctionRegistry.java
+++ 
b/pinot-common/src/main/java/org/apache/pinot/common/function/FunctionRegistry.java
@@ -18,18 +18,17 @@
  */
 package org.apache.pinot.common.function;
 
-import com.google.common.base.Preconditions;
 import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
+import javax.annotation.Nullable;
 import org.apache.pinot.common.function.annotations.ScalarFunction;
 import org.reflections.Reflections;
 import org.reflections.scanners.MethodAnnotationsScanner;
-import org.reflections.scanners.SubTypesScanner;
-import org.reflections.scanners.TypeAnnotationsScanner;
 import org.reflections.util.ClasspathHelper;
 import org.reflections.util.ConfigurationBuilder;
+import org.reflections.util.FilterBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -38,63 +37,72 @@ import org.slf4j.LoggerFactory;
  * Registry for in-built Pinot functions
  */
 public class FunctionRegistry {
+  private FunctionRegistry() {
+  }
+
   private static final Logger LOGGER = 
LoggerFactory.getLogger(FunctionRegistry.class);
-  private static final Map<String, FunctionInfo> _functionInfoMap = new 
HashMap<>();
+  private static final Map<String, FunctionInfo> FUNCTION_INFO_MAP = new 
HashMap<>();
 
   /**
-   * Given a function name, asserts that a corresponding function was 
registered during construction and returns it
+   * Registers the scalar functions via reflection.
+   * NOTE: In order to plugin methods using reflection, the methods should be 
inside a class that includes ".function."
+   *       in its class path. This convention can significantly reduce the 
time of class scanning.
    */
-  public static FunctionInfo getFunctionByName(String functionName) {
-    
Preconditions.checkArgument(_functionInfoMap.containsKey(functionName.toLowerCase()));
-    return _functionInfoMap.get(functionName.toLowerCase());
+  static {
+    long startTimeMs = System.currentTimeMillis();
+    Reflections reflections = new Reflections(
+        new 
ConfigurationBuilder().setUrls(ClasspathHelper.forPackage("org.apache.pinot"))
+            .filterInputsBy(new FilterBuilder.Include(".*\\.function\\..*"))
+            .setScanners(new MethodAnnotationsScanner()));
+    Set<Method> methodSet = 
reflections.getMethodsAnnotatedWith(ScalarFunction.class);
+    for (Method method : methodSet) {
+      ScalarFunction scalarFunction = 
method.getAnnotation(ScalarFunction.class);
+      if (scalarFunction.enabled()) {
+        if (!scalarFunction.name().isEmpty()) {
+          FunctionRegistry.registerFunction(scalarFunction.name(), method);
+        } else {
+          FunctionRegistry.registerFunction(method);
+        }
+      }
+    }
+    LOGGER.info("Initialized FunctionRegistry with {} functions: {} in {}ms", 
FUNCTION_INFO_MAP.size(),
+        FUNCTION_INFO_MAP.keySet(), System.currentTimeMillis() - startTimeMs);
   }
 
   /**
-   * Given a function name and a set of argument types, asserts that a 
corresponding function
-   * was registered during construction and returns it
+   * Initializes the FunctionRegistry.
+   * NOTE: This method itself is a NO-OP, but can be used to explicitly 
trigger the static block of registering the
+   *       scalar functions via reflection.
    */
-  public static FunctionInfo 
getFunctionByNameWithApplicableArgumentTypes(String functionName,
-      Class<?>[] argumentTypes) {
-    FunctionInfo functionInfo = getFunctionByName(functionName);
-    Preconditions.checkArgument(functionInfo.isApplicable(argumentTypes));
-    return functionInfo;
+  public static void init() {
   }
 
+  /**
+   * Registers a method with the name of the method.
+   */
   public static void registerFunction(Method method) {
-    registerFunction(method.getName().toLowerCase(), method);
+    registerFunction(method.getName(), method);
   }
 
-  public static void registerFunction(String name, Method method) {
+  /**
+   * Registers a method with the given function name.
+   */
+  public static void registerFunction(String functionName, Method method) {
     FunctionInfo functionInfo = new FunctionInfo(method, 
method.getDeclaringClass());
-    _functionInfoMap.put(name, functionInfo);
+    FUNCTION_INFO_MAP.put(canonicalize(functionName), functionInfo);
   }
 
-  public static boolean containsFunctionByName(String funcName) {
-    return _functionInfoMap.containsKey(funcName.toLowerCase());
+  /**
+   * Returns the {@link FunctionInfo} associated with the given function name, 
or {@code null} if there is no method
+   * registered under the name. This method should be called after the 
FunctionRegistry is initialized and all methods
+   * are already registered.
+   */
+  @Nullable
+  public static FunctionInfo getFunctionByName(String functionName) {
+    return FUNCTION_INFO_MAP.get(canonicalize(functionName));
   }
 
-  static {
-    try {
-
-      Reflections reflections = new Reflections(
-          new 
ConfigurationBuilder().setUrls(ClasspathHelper.forPackage("org.apache.pinot"))
-              .setScanners(new MethodAnnotationsScanner()));
-
-      Set<Method> methodSet = 
reflections.getMethodsAnnotatedWith(ScalarFunction.class);
-      for (Method method : methodSet) {
-        ScalarFunction scalarFunction = 
method.getAnnotation(ScalarFunction.class);
-        if (scalarFunction.enabled()) {
-          if (!scalarFunction.name().isEmpty()) {
-            FunctionRegistry.registerFunction(scalarFunction.name(), method);
-          } else {
-            FunctionRegistry.registerFunction(method);
-          }
-        }
-      }
-
-    } catch (Exception e) {
-      LOGGER.error("Caught exception when registering function", e);
-      throw new IllegalStateException(e);
-    }
+  private static String canonicalize(String functionName) {
+    return functionName.toLowerCase();
   }
 }
diff --git 
a/pinot-common/src/main/java/org/apache/pinot/sql/parsers/CalciteSqlParser.java 
b/pinot-common/src/main/java/org/apache/pinot/sql/parsers/CalciteSqlParser.java
index 2aec8ec..de05274 100644
--- 
a/pinot-common/src/main/java/org/apache/pinot/sql/parsers/CalciteSqlParser.java
+++ 
b/pinot-common/src/main/java/org/apache/pinot/sql/parsers/CalciteSqlParser.java
@@ -628,8 +628,7 @@ public class CalciteSqlParser {
       funcName = funcSqlNode.getOperator().getName();
     }
     if (funcName.equalsIgnoreCase(SqlKind.COUNT.toString()) && 
(funcSqlNode.getFunctionQuantifier() != null)
-        && funcSqlNode.getFunctionQuantifier().toValue()
-        .equalsIgnoreCase(AggregationFunctionType.DISTINCT.getName())) {
+        && 
funcSqlNode.getFunctionQuantifier().toValue().equalsIgnoreCase(AggregationFunctionType.DISTINCT.getName()))
 {
       funcName = AggregationFunctionType.DISTINCTCOUNT.getName();
     }
     return funcName;
@@ -667,21 +666,23 @@ public class CalciteSqlParser {
       function.getOperands().set(i, operand);
     }
     String funcName = function.getOperator();
-    if (FunctionRegistry.containsFunctionByName(funcName) && compilable) {
+    if (compilable) {
       FunctionInfo functionInfo = FunctionRegistry.getFunctionByName(funcName);
-      Object[] arguments = new Object[functionOperandsLength];
-      for (int i = 0; i < functionOperandsLength; i++) {
-        arguments[i] = 
function.getOperands().get(i).getLiteral().getFieldValue();
-      }
-      try {
-        FunctionInvoker invoker = new FunctionInvoker(functionInfo);
-        Object result = invoker.process(arguments);
-        if (result instanceof String) {
-          result = String.format("'%s'", result);
+      if (functionInfo != null) {
+        Object[] arguments = new Object[functionOperandsLength];
+        for (int i = 0; i < functionOperandsLength; i++) {
+          arguments[i] = 
function.getOperands().get(i).getLiteral().getFieldValue();
+        }
+        try {
+          FunctionInvoker invoker = new FunctionInvoker(functionInfo);
+          Object result = invoker.process(arguments);
+          if (result instanceof String) {
+            result = String.format("'%s'", result);
+          }
+          return RequestUtils.getLiteralExpression(result);
+        } catch (Exception e) {
+          throw new SqlCompilationException(new 
IllegalArgumentException("Unsupported function - " + funcName, e));
         }
-        return RequestUtils.getLiteralExpression(result);
-      } catch (Exception e) {
-        throw new SqlCompilationException(new 
IllegalArgumentException("Unsupported function - " + funcName, e));
       }
     }
     return funcExpr;
diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerStarter.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerStarter.java
index 75e766e..1eab79a 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerStarter.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerStarter.java
@@ -44,6 +44,7 @@ import 
org.apache.helix.api.listeners.ControllerChangeListener;
 import org.apache.helix.model.MasterSlaveSMD;
 import org.apache.helix.task.TaskDriver;
 import org.apache.pinot.common.Utils;
+import org.apache.pinot.common.function.FunctionRegistry;
 import org.apache.pinot.common.metrics.ControllerMeter;
 import org.apache.pinot.common.metrics.ControllerMetrics;
 import org.apache.pinot.common.metrics.MetricsHelper;
@@ -151,6 +152,9 @@ public class ControllerStarter implements ServiceStartable {
       _helixResourceManager = null;
       _executorService = null;
     } else {
+      // Initialize FunctionRegistry before starting the admin application 
(PinotQueryResource requires it to compile
+      // queries)
+      FunctionRegistry.init();
       _adminApp =
           new 
ControllerAdminApiApplication(_config.getQueryConsoleWebappPath(), 
_config.getQueryConsoleUseHttps());
       // Do not use this before the invocation of {@link 
PinotHelixResourceManager::start()}, which happens in {@link 
ControllerStarter::start()}
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluator.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluator.java
index e49d6c4..e427355 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluator.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluator.java
@@ -20,6 +20,7 @@ package org.apache.pinot.core.data.function;
 
 import com.google.common.base.Preconditions;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import org.apache.commons.lang3.math.NumberUtils;
 import org.apache.pinot.common.function.FunctionInfo;
@@ -86,8 +87,9 @@ public class InbuiltFunctionEvaluator implements 
FunctionEvaluator {
       argumentTypes[i] = childNode.getReturnType();
     }
 
-    FunctionInfo functionInfo =
-        
FunctionRegistry.getFunctionByNameWithApplicableArgumentTypes(transformName, 
argumentTypes);
+    FunctionInfo functionInfo = 
FunctionRegistry.getFunctionByName(transformName);
+    Preconditions.checkState(functionInfo != null && 
functionInfo.isApplicable(argumentTypes),
+        "Failed to find function of name: %s with argument types: %s", 
transformName, Arrays.toString(argumentTypes));
     return new FunctionExecutionNode(functionInfo, childNodes);
   }
 
@@ -114,8 +116,6 @@ public class InbuiltFunctionEvaluator implements 
FunctionEvaluator {
 
     public FunctionExecutionNode(FunctionInfo functionInfo, ExecutableNode[] 
argumentProviders)
         throws Exception {
-      Preconditions.checkNotNull(functionInfo);
-      Preconditions.checkNotNull(argumentProviders);
       _functionInvoker = new FunctionInvoker(functionInfo);
       _argumentProviders = argumentProviders;
       _argInputs = new Object[_argumentProviders.length];
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TransformFunctionFactory.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TransformFunctionFactory.java
index 02b41bc..7084c61 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TransformFunctionFactory.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TransformFunctionFactory.java
@@ -111,30 +111,30 @@ public class TransformFunctionFactory {
    * @return Transform function
    */
   public static TransformFunction get(TransformExpressionTree expression, 
Map<String, DataSource> dataSourceMap) {
-    TransformFunction transformFunction;
     switch (expression.getExpressionType()) {
       case FUNCTION:
+        TransformFunction transformFunction;
         String functionName = expression.getValue();
-        Class<? extends TransformFunction> transformFunctionClass;
-        FunctionInfo functionInfo = null;
-        if (FunctionRegistry.containsFunctionByName(functionName)) {
-          transformFunctionClass = ScalarTransformFunctionWrapper.class;
-          functionInfo = FunctionRegistry.getFunctionByName(functionName);
+        Class<? extends TransformFunction> transformFunctionClass = 
TRANSFORM_FUNCTION_MAP.get(functionName);
+        if (transformFunctionClass != null) {
+          // Transform function
+          try {
+            transformFunction = transformFunctionClass.newInstance();
+          } catch (Exception e) {
+            throw new RuntimeException("Caught exception while constructing 
transform function: " + functionName, e);
+          }
         } else {
-          transformFunctionClass = TRANSFORM_FUNCTION_MAP.get(functionName);
-        }
-
-        if (transformFunctionClass == null) {
-          throw new BadQueryRequestException("Unsupported transform function: 
" + functionName);
-        }
-        try {
-          if (functionInfo != null) {
+          // Scalar function
+          FunctionInfo functionInfo = 
FunctionRegistry.getFunctionByName(functionName);
+          if (functionInfo == null) {
+            throw new BadQueryRequestException("Unsupported transform 
function: " + functionName);
+          }
+          try {
             transformFunction = new 
ScalarTransformFunctionWrapper(functionName, functionInfo);
-          } else {
-            transformFunction = transformFunctionClass.newInstance();
+          } catch (Exception e) {
+            throw new RuntimeException("Caught exception while constructing 
scalar transform function: " + functionName,
+                e);
           }
-        } catch (Exception e) {
-          throw new RuntimeException("Caught exception while instantiating 
transform function: " + functionName, e);
         }
         List<TransformExpressionTree> children = expression.getChildren();
         List<TransformFunction> arguments = new ArrayList<>(children.size());
@@ -144,8 +144,8 @@ public class TransformFunctionFactory {
         try {
           transformFunction.init(arguments, dataSourceMap);
         } catch (Exception e) {
-          throw new BadQueryRequestException(
-              "Caught exception while initializing transform function: " + 
transformFunction.getName(), e);
+          throw new BadQueryRequestException("Caught exception while 
initializing transform function: " + functionName,
+              e);
         }
         return transformFunction;
       case IDENTIFIER:
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluatorTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluatorTest.java
index b89f1e2..e5f34b4 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluatorTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluatorTest.java
@@ -19,7 +19,6 @@
 package org.apache.pinot.core.data.function;
 
 import com.google.common.collect.Lists;
-import org.apache.pinot.common.function.FunctionInfo;
 import org.apache.pinot.common.function.FunctionRegistry;
 import org.apache.pinot.spi.data.readers.GenericRow;
 import org.joda.time.DateTime;
@@ -37,11 +36,7 @@ public class InbuiltFunctionEvaluatorTest {
       throws Exception {
     MyFunc myFunc = new MyFunc();
     
FunctionRegistry.registerFunction(myFunc.getClass().getDeclaredMethod("reverseString",
 String.class));
-    FunctionInfo functionInfo = FunctionRegistry
-        .getFunctionByNameWithApplicableArgumentTypes("reverseString", new 
Class<?>[]{Object.class});
-    System.out.println(functionInfo);
     String expression = "reverseString(testColumn)";
-
     InbuiltFunctionEvaluator evaluator = new 
InbuiltFunctionEvaluator(expression);
     Assert.assertEquals(evaluator.getArguments(), 
Lists.newArrayList("testColumn"));
     GenericRow row = new GenericRow();
@@ -91,8 +86,7 @@ public class InbuiltFunctionEvaluatorTest {
   public void testStateSharedBetweenRowsForExecution()
       throws Exception {
     MyFunc myFunc = new MyFunc();
-    FunctionRegistry
-        
.registerFunction(myFunc.getClass().getDeclaredMethod("appendToStringAndReturn",
 String.class));
+    
FunctionRegistry.registerFunction(myFunc.getClass().getDeclaredMethod("appendToStringAndReturn",
 String.class));
     String expression = String.format("appendToStringAndReturn('%s')", "test 
");
     InbuiltFunctionEvaluator evaluator = new 
InbuiltFunctionEvaluator(expression);
     Assert.assertTrue(evaluator.getArguments().isEmpty());
@@ -101,28 +95,28 @@ public class InbuiltFunctionEvaluatorTest {
     Assert.assertEquals(evaluator.evaluate(row), "test test ");
     Assert.assertEquals(evaluator.evaluate(row), "test test test ");
   }
-}
 
-class MyFunc {
-  String reverseString(String input) {
-    return new StringBuilder(input).reverse().toString();
-  }
+  private static class MyFunc {
+    String reverseString(String input) {
+      return new StringBuilder(input).reverse().toString();
+    }
 
-  MutableDateTime EPOCH_START = new MutableDateTime();
+    MutableDateTime EPOCH_START = new MutableDateTime();
 
-  public MyFunc() {
-    EPOCH_START.setDate(0L);
-  }
+    public MyFunc() {
+      EPOCH_START.setDate(0L);
+    }
 
-  int daysSinceEpoch(String input, String format) {
-    DateTime dateTime = DateTimeFormat.forPattern(format).parseDateTime(input);
-    return Days.daysBetween(EPOCH_START, dateTime).getDays();
-  }
+    int daysSinceEpoch(String input, String format) {
+      DateTime dateTime = 
DateTimeFormat.forPattern(format).parseDateTime(input);
+      return Days.daysBetween(EPOCH_START, dateTime).getDays();
+    }
 
-  private String baseString = "";
+    private String baseString = "";
 
-  String appendToStringAndReturn(String addedString) {
-    baseString += addedString;
-    return baseString;
+    String appendToStringAndReturn(String addedString) {
+      baseString += addedString;
+      return baseString;
+    }
   }
 }
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionsTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionsTest.java
index 9532ea8..a156089 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionsTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionsTest.java
@@ -230,8 +230,7 @@ public class InbuiltFunctionsTest {
 
     GenericRow row1 = new GenericRow();
     jsonStr = 
"{\"k3\":{\"sub1\":10,\"sub2\":1.0},\"k4\":\"baz\",\"k5\":[1,2,3]}";
-    row1.putValue("jsonMap", JsonUtils
-        .stringToObject(jsonStr, Map.class));
+    row1.putValue("jsonMap", JsonUtils.stringToObject(jsonStr, Map.class));
     inputs.add(new Object[]{"toJsonMapStr(jsonMap)", 
Lists.newArrayList("jsonMap"), row1, jsonStr});
 
     GenericRow row2 = new GenericRow();
@@ -241,20 +240,18 @@ public class InbuiltFunctionsTest {
 
     GenericRow row3 = new GenericRow();
     jsonStr = 
"{\"k3\":{\"sub1\":10,\"sub2\":1.0},\"k4\":\"baz\",\"k5\":[1,2,3]}";
-    row3.putValue("jsonMap", JsonUtils
-        .stringToObject(jsonStr, Map.class));
+    row3.putValue("jsonMap", JsonUtils.stringToObject(jsonStr, Map.class));
     inputs.add(new Object[]{"json_format(jsonMap)", 
Lists.newArrayList("jsonMap"), row3, jsonStr});
 
     GenericRow row4 = new GenericRow();
     jsonStr = "[{\"one\":1,\"two\":\"too\"},{\"one\":11,\"two\":\"roo\"}]";
-    row4.putValue("jsonMap", JsonUtils
-        .stringToObject(jsonStr, List.class));
+    row4.putValue("jsonMap", JsonUtils.stringToObject(jsonStr, List.class));
     inputs.add(new Object[]{"json_format(jsonMap)", 
Lists.newArrayList("jsonMap"), row4, jsonStr});
 
     GenericRow row5 = new GenericRow();
-    jsonStr = 
"[{\"one\":1,\"two\":{\"sub1\":1.1,\"sub2\":1.2},\"three\":[\"a\",\"b\"]},{\"one\":11,\"two\":{\"sub1\":11.1,\"sub2\":11.2},\"three\":[\"aa\",\"bb\"]}]";
-    row5.putValue("jsonMap", JsonUtils
-        .stringToObject(jsonStr, List.class));
+    jsonStr =
+        
"[{\"one\":1,\"two\":{\"sub1\":1.1,\"sub2\":1.2},\"three\":[\"a\",\"b\"]},{\"one\":11,\"two\":{\"sub1\":11.1,\"sub2\":11.2},\"three\":[\"aa\",\"bb\"]}]";
+    row5.putValue("jsonMap", JsonUtils.stringToObject(jsonStr, List.class));
     inputs.add(new Object[]{"json_format(jsonMap)", 
Lists.newArrayList("jsonMap"), row5, jsonStr});
 
     return inputs.toArray(new Object[0][]);
diff --git 
a/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerInstance.java
 
b/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerInstance.java
index 7a11728..ae448ad 100644
--- 
a/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerInstance.java
+++ 
b/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerInstance.java
@@ -24,6 +24,7 @@ import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.atomic.LongAccumulator;
 import org.apache.helix.HelixManager;
+import org.apache.pinot.common.function.FunctionRegistry;
 import org.apache.pinot.common.metrics.MetricsHelper;
 import org.apache.pinot.common.metrics.ServerMetrics;
 import org.apache.pinot.core.data.manager.InstanceDataManager;
@@ -71,6 +72,8 @@ public class ServerInstance {
     _instanceDataManager = (InstanceDataManager) 
Class.forName(instanceDataManagerClassName).newInstance();
     _instanceDataManager.init(serverConf.getInstanceDataManagerConfig(), 
helixManager, _serverMetrics);
 
+    // Initialize FunctionRegistry before starting the query executor
+    FunctionRegistry.init();
     String queryExecutorClassName = serverConf.getQueryExecutorClassName();
     LOGGER.info("Initializing query executor of class: {}", 
queryExecutorClassName);
     _queryExecutor = (QueryExecutor) 
Class.forName(queryExecutorClassName).newInstance();


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org
For additional commands, e-mail: commits-h...@pinot.apache.org

Reply via email to