You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pinot.apache.org by ki...@apache.org on 2020/05/18 07:25:35 UTC

[incubator-pinot] 01/01: Adding support to execute functions during query compilation

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

kishoreg pushed a commit to branch time-functions-in-query
in repository https://gitbox.apache.org/repos/asf/incubator-pinot.git

commit b91b4f33292c594307fe5e7087beae7f13dc0531
Author: kishoreg <g....@gmail.com>
AuthorDate: Mon May 18 00:25:02 2020 -0700

    Adding support to execute functions during query compilation
---
 .../function/FunctionDefinitionRegistry.java       |  4 ++
 .../pinot/common/function/ScalarFunctionType.java  | 69 ++++++++++++++++++++++
 .../apache/pinot/sql/parsers/CalciteSqlParser.java | 45 +++++++++++++-
 3 files changed, 117 insertions(+), 1 deletion(-)

diff --git a/pinot-common/src/main/java/org/apache/pinot/common/function/FunctionDefinitionRegistry.java b/pinot-common/src/main/java/org/apache/pinot/common/function/FunctionDefinitionRegistry.java
index 8e70a20..25381c3 100644
--- a/pinot-common/src/main/java/org/apache/pinot/common/function/FunctionDefinitionRegistry.java
+++ b/pinot-common/src/main/java/org/apache/pinot/common/function/FunctionDefinitionRegistry.java
@@ -40,4 +40,8 @@ public class FunctionDefinitionRegistry {
       return false;
     }
   }
+
+  public static boolean isScalarFunc(String functionName) {
+    return ScalarFunctionType.isScalarFunctionType(functionName);
+  }
 }
diff --git a/pinot-common/src/main/java/org/apache/pinot/common/function/ScalarFunctionType.java b/pinot-common/src/main/java/org/apache/pinot/common/function/ScalarFunctionType.java
new file mode 100644
index 0000000..c2c88a7
--- /dev/null
+++ b/pinot-common/src/main/java/org/apache/pinot/common/function/ScalarFunctionType.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.common.function;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+
+public enum ScalarFunctionType {
+
+  NOW("now"), FORMAT_DATETIME("format_datetime");
+
+  private final String _name;
+
+  static HashMap<String, ScalarFunctionType> _scalarFunctions = new HashMap<>();
+
+  static {
+    ScalarFunctionType[] values = ScalarFunctionType.values();
+    for (ScalarFunctionType value : values) {
+      String upperCaseFunctionName = value.getName().toUpperCase();
+      _scalarFunctions.put(upperCaseFunctionName, value);
+      _scalarFunctions.put(upperCaseFunctionName.replace("_", ""), value);
+    }
+  }
+
+  ScalarFunctionType(String name) {
+    _name = name;
+  }
+
+  /**
+   * Returns the corresponding transform function type for the given function name.
+   */
+  public static ScalarFunctionType getScalarFunctionType(String functionName) {
+    String upperCaseFunctionName = functionName.toUpperCase();
+    try {
+      return ScalarFunctionType.valueOf(upperCaseFunctionName);
+    } catch (Exception e) {
+      // Support function name of both datetime_format and datetime_format
+      if (upperCaseFunctionName.contains("_")) {
+        return getScalarFunctionType(upperCaseFunctionName.replace("_", ""));
+      }
+      throw new IllegalArgumentException("Invalid scalar function name: " + functionName);
+    }
+  }
+
+  public static boolean isScalarFunctionType(String functionName) {
+    return _scalarFunctions.containsKey(functionName.toUpperCase());
+  }
+
+  public String getName() {
+    return _name;
+  }
+}
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 5abffc3..f63b703 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
@@ -18,6 +18,7 @@
  */
 package org.apache.pinot.sql.parsers;
 
+import com.google.common.base.Preconditions;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -45,6 +46,7 @@ import org.apache.calcite.sql.parser.babel.SqlBabelParserImpl;
 import org.apache.calcite.sql.validate.SqlConformanceEnum;
 import org.apache.pinot.common.function.AggregationFunctionType;
 import org.apache.pinot.common.function.FunctionDefinitionRegistry;
+import org.apache.pinot.common.function.ScalarFunctionType;
 import org.apache.pinot.common.request.DataSource;
 import org.apache.pinot.common.request.Expression;
 import org.apache.pinot.common.request.ExpressionType;
@@ -52,6 +54,7 @@ import org.apache.pinot.common.request.Function;
 import org.apache.pinot.common.request.Identifier;
 import org.apache.pinot.common.request.PinotQuery;
 import org.apache.pinot.common.utils.request.RequestUtils;
+import org.joda.time.format.DateTimeFormat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -603,7 +606,7 @@ public class CalciteSqlParser {
             .equalsIgnoreCase(AggregationFunctionType.DISTINCT.getName())) {
           funcName = AggregationFunctionType.DISTINCTCOUNT.getName();
         }
-        final Expression funcExpr = RequestUtils.getFunctionExpression(funcName);
+        Expression funcExpr = RequestUtils.getFunctionExpression(funcName);
         for (SqlNode child : funcSqlNode.getOperands()) {
           if (child instanceof SqlNodeList) {
             final Iterator<SqlNode> iterator = ((SqlNodeList) child).iterator();
@@ -615,7 +618,47 @@ public class CalciteSqlParser {
             funcExpr.getFunctionCall().addToOperands(toExpression(child));
           }
         }
+
+        if (FunctionDefinitionRegistry.isScalarFunc(funcName) && isCompileTimeEvaluationPossible(funcExpr)) {
+
+          ScalarFunctionType scalarFunctionType = ScalarFunctionType.getScalarFunctionType(funcName);
+          switch (scalarFunctionType) {
+            case NOW:
+              funcExpr = RequestUtils.getLiteralExpression(System.currentTimeMillis());
+              break;
+            case FORMAT_DATETIME:
+              //DATETIME_FORMAT ('2020-01-01', 'yyyy-MM-dd')
+              String input = funcExpr.getFunctionCall().getOperands().get(0).getLiteral().getStringValue();
+              String format = funcExpr.getFunctionCall().getOperands().get(1).getLiteral().getStringValue();
+              long output = DateTimeFormat.forPattern(format).parseMillis(input);
+              funcExpr = RequestUtils.getLiteralExpression(output);
+              break;
+            default:
+              //no change, let the expression be handled during execution phase
+          }
+        }
+
         return funcExpr;
     }
   }
+
+  /**
+   * Utility method to check if the function can be evaluated during the query compilation phae
+   * @param funcExpr
+   * @return true if all arguments are literals
+   */
+  private static boolean isCompileTimeEvaluationPossible(Expression funcExpr) {
+
+    boolean compileTimeEvaluationPossible = true;
+    Function functionCall = funcExpr.getFunctionCall();
+    if(functionCall.getOperandsSize() > 0) {
+      for (Expression expression : functionCall.getOperands()) {
+        if (expression.getType() != ExpressionType.LITERAL) {
+          compileTimeEvaluationPossible = false;
+          break;
+        }
+      }
+    }
+    return compileTimeEvaluationPossible;
+  }
 }


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