You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2017/07/06 23:00:39 UTC

[2/7] calcite git commit: [CALCITE-1833] User-defined aggregate functions with more than one parameter (hzyuemeng1)

[CALCITE-1833] User-defined aggregate functions with more than one parameter (hzyuemeng1)

I don't like the fact that SqlUserDefinedAggregateFunction now has a
type factory field. I see why it is necessary - because the
SqlFunction.getParamTypes method does not take a type factory argument -
but we should consider changing it in future.

Close apache/calcite#481


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/47c8df9e
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/47c8df9e
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/47c8df9e

Branch: refs/heads/master
Commit: 47c8df9ecad8aa0c8a65530dd89b94b3749f54a2
Parents: f474652
Author: hzyuemeng1 <hz...@corp.netease.com>
Authored: Tue Jun 20 09:51:57 2017 +0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Jul 6 11:24:37 2017 -0700

----------------------------------------------------------------------
 .../calcite/prepare/CalciteCatalogReader.java   |  2 +-
 .../sql/validate/SqlUserDefinedAggFunction.java | 36 ++++++++-
 .../java/org/apache/calcite/test/UdfTest.java   | 81 +++++++++++++++++--
 .../java/org/apache/calcite/util/Smalls.java    | 82 ++++++++++++++++++++
 4 files changed, 194 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/47c8df9e/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
index 9ec3df4..921191a 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
@@ -329,7 +329,7 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
     } else if (function instanceof AggregateFunction) {
       return new SqlUserDefinedAggFunction(name,
           infer((AggregateFunction) function), InferTypes.explicit(argTypes),
-          typeChecker, (AggregateFunction) function, false, false);
+          typeChecker, (AggregateFunction) function, false, false, typeFactory);
     } else if (function instanceof TableMacro) {
       return new SqlUserDefinedTableMacro(name, ReturnTypes.CURSOR,
           InferTypes.explicit(argTypes), typeChecker, paramTypes,

http://git-wip-us.apache.org/repos/asf/calcite/blob/47c8df9e/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
index da1f833..a8be50b 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
@@ -16,8 +16,10 @@
  */
 package org.apache.calcite.sql.validate;
 
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeFactoryImpl;
 import org.apache.calcite.schema.AggregateFunction;
 import org.apache.calcite.schema.FunctionParameter;
 import org.apache.calcite.sql.SqlAggFunction;
@@ -27,11 +29,13 @@ import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.type.SqlOperandTypeChecker;
 import org.apache.calcite.sql.type.SqlOperandTypeInference;
 import org.apache.calcite.sql.type.SqlReturnTypeInference;
+import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.Util;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Lists;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -42,17 +46,47 @@ import java.util.List;
  */
 public class SqlUserDefinedAggFunction extends SqlAggFunction {
   public final AggregateFunction function;
+  public final RelDataTypeFactory typeFactory;
 
   /** Creates a SqlUserDefinedAggFunction. */
   public SqlUserDefinedAggFunction(SqlIdentifier opName,
       SqlReturnTypeInference returnTypeInference,
       SqlOperandTypeInference operandTypeInference,
       SqlOperandTypeChecker operandTypeChecker, AggregateFunction function,
-      boolean requiresOrder, boolean requiresOver) {
+      boolean requiresOrder, boolean requiresOver, RelDataTypeFactory typeFactory) {
     super(Util.last(opName.names), opName, SqlKind.OTHER_FUNCTION,
         returnTypeInference, operandTypeInference, operandTypeChecker,
         SqlFunctionCategory.USER_DEFINED_FUNCTION, requiresOrder, requiresOver);
     this.function = function;
+    this.typeFactory = typeFactory;
+  }
+
+  @Override public List<RelDataType> getParamTypes() {
+    List<RelDataType> argTypes = new ArrayList<>();
+    for (FunctionParameter o : function.getParameters()) {
+      final RelDataType type = o.getType(typeFactory);
+      argTypes.add(type);
+    }
+    return toSql(argTypes);
+  }
+
+  private List<RelDataType> toSql(List<RelDataType> types) {
+    return Lists.transform(types,
+        new com.google.common.base.Function<RelDataType, RelDataType>() {
+          public RelDataType apply(RelDataType type) {
+            return toSql(type);
+          }
+        });
+  }
+
+  private RelDataType toSql(RelDataType type) {
+    if (type instanceof RelDataTypeFactoryImpl.JavaType
+        && ((RelDataTypeFactoryImpl.JavaType) type).getJavaClass()
+        == Object.class) {
+      return typeFactory.createTypeWithNullability(
+          typeFactory.createSqlType(SqlTypeName.ANY), true);
+    }
+    return JavaTypeFactoryImpl.toSql(typeFactory, type);
   }
 
   @SuppressWarnings("deprecation")

http://git-wip-us.apache.org/repos/asf/calcite/blob/47c8df9e/core/src/test/java/org/apache/calcite/test/UdfTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/UdfTest.java b/core/src/test/java/org/apache/calcite/test/UdfTest.java
index 3297c27..3d0879f 100644
--- a/core/src/test/java/org/apache/calcite/test/UdfTest.java
+++ b/core/src/test/java/org/apache/calcite/test/UdfTest.java
@@ -463,8 +463,7 @@ public class UdfTest {
     with.query("select my_sum(\"deptno\") as p from EMPLOYEES\n")
         .returns("P=50\n");
     with.query("select my_sum(\"name\") as p from EMPLOYEES\n")
-        .throws_(
-            "Cannot apply 'MY_SUM' to arguments of type 'MY_SUM(<JAVATYPE(CLASS JAVA.LANG.STRING)>)'. Supported form(s): 'MY_SUM(<NUMERIC>)");
+        .throws_("No match found for function signature MY_SUM(<CHARACTER>)");
     with.query("select my_sum(\"deptno\", 1) as p from EMPLOYEES\n")
         .throws_(
             "No match found for function signature MY_SUM(<NUMERIC>, <NUMERIC>)");
@@ -481,6 +480,80 @@ public class UdfTest {
         .returnsUnordered("deptno=20; P=20", "deptno=10; P=30");
   }
 
+  /** Tests user-defined aggregate function. */
+  @Test public void testUserDefinedAggregateFunctionWithMultipleParameters() throws Exception {
+    final String empDept = JdbcTest.EmpDeptTableFactory.class.getName();
+    final String sum21 = Smalls.MyTwoParamsSumFunctionFilter1.class.getName();
+    final String sum22 = Smalls.MyTwoParamsSumFunctionFilter2.class.getName();
+    final String sum31 = Smalls.MyThreeParamsSumFunctionWithFilter1.class.getName();
+    final String sum32 = Smalls.MyThreeParamsSumFunctionWithFilter2.class.getName();
+    final CalciteAssert.AssertThat with = CalciteAssert.model("{\n"
+        + "  version: '1.0',\n"
+        + "   schemas: [\n"
+        + "     {\n"
+        + "       name: 'adhoc',\n"
+        + "       tables: [\n"
+        + "         {\n"
+        + "           name: 'EMPLOYEES',\n"
+        + "           type: 'custom',\n"
+        + "           factory: '" + empDept + "',\n"
+        + "           operand: {'foo': true, 'bar': 345}\n"
+        + "         }\n"
+        + "       ],\n"
+        + "       functions: [\n"
+        + "         {\n"
+        + "           name: 'MY_SUM2',\n"
+        + "           className: '" + sum21 + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'MY_SUM2',\n"
+        + "           className: '" + sum22 + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'MY_SUM3',\n"
+        + "           className: '" + sum31 + "'\n"
+        + "         },\n"
+        + "         {\n"
+        + "           name: 'MY_SUM3',\n"
+        + "           className: '" + sum32 + "'\n"
+        + "         }\n"
+        + "       ]\n"
+        + "     }\n"
+        + "   ]\n"
+        + "}")
+        .withDefaultSchema("adhoc");
+    with.withDefaultSchema(null)
+        .query(
+            "select \"adhoc\".my_sum3(\"deptno\",\"name\",'Eric') as p from \"adhoc\".EMPLOYEES\n")
+        .returns("P=20\n");
+    with.query("select \"adhoc\".my_sum3(\"empid\",\"deptno\",\"commission\") as p "
+        + "from \"adhoc\".EMPLOYEES\n")
+        .returns("P=330\n");
+    with.query("select \"adhoc\".my_sum3(\"empid\",\"deptno\",\"commission\"),\"name\" as p "
+        + "from \"adhoc\".EMPLOYEES\n")
+        .throws_("Expression 'name' is not being grouped");
+    with.query("select \"name\",\"adhoc\".my_sum3(\"empid\",\"deptno\",\"commission\") as p "
+        + "from \"adhoc\".EMPLOYEES\n"
+        + "group by \"name\"")
+        .returnsUnordered("name=Theodore; P=0",
+            "name=Eric; P=220",
+            "name=Bill; P=110",
+            "name=Sebastian; P=0");
+    with.query("select \"adhoc\".my_sum3(\"empid\",\"deptno\",\"salary\") as p "
+        + "from \"adhoc\".EMPLOYEES\n")
+        .throws_("No match found for function signature MY_SUM3(<NUMERIC>, "
+            + "<NUMERIC>, <APPROXIMATE_NUMERIC>)");
+    with.query("select \"adhoc\".my_sum3(\"empid\",\"deptno\",\"name\") as p "
+        + "from \"adhoc\".EMPLOYEES\n");
+    with.query("select \"adhoc\".my_sum2(\"commission\",250) as p "
+        + "from \"adhoc\".EMPLOYEES\n")
+        .returns("P=1500\n");
+    with.query("select \"adhoc\".my_sum2(\"name\",250) as p from \"adhoc\".EMPLOYEES\n")
+        .throws_("No match found for function signature MY_SUM2(<CHARACTER>, <NUMERIC>)");
+    with.query("select \"adhoc\".my_sum2(\"empid\",0.0) as p from \"adhoc\".EMPLOYEES\n")
+        .throws_("No match found for function signature MY_SUM2(<NUMERIC>, <NUMERIC>)");
+  }
+
   /** Test for
    * {@link org.apache.calcite.runtime.CalciteResource#firstParameterOfAdd(String)}. */
   @Test public void testUserDefinedAggregateFunction3() throws Exception {
@@ -531,9 +604,7 @@ public class UdfTest {
     with.query("select my_sum3(\"deptno\") as p from EMPLOYEES\n")
         .returns("P=50\n");
     with.query("select my_sum3(\"name\") as p from EMPLOYEES\n")
-        .throws_("Cannot apply 'MY_SUM3' to arguments of type "
-            + "'MY_SUM3(<JAVATYPE(CLASS JAVA.LANG.STRING)>)'. "
-            + "Supported form(s): 'MY_SUM3(<NUMERIC>)");
+        .throws_("No match found for function signature MY_SUM3(<CHARACTER>)");
     with.query("select my_sum3(\"deptno\", 1) as p from EMPLOYEES\n")
         .throws_("No match found for function signature "
             + "MY_SUM3(<NUMERIC>, <NUMERIC>)");

http://git-wip-us.apache.org/repos/asf/calcite/blob/47c8df9e/core/src/test/java/org/apache/calcite/util/Smalls.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/util/Smalls.java b/core/src/test/java/org/apache/calcite/util/Smalls.java
index c10d119..be5bd3e 100644
--- a/core/src/test/java/org/apache/calcite/util/Smalls.java
+++ b/core/src/test/java/org/apache/calcite/util/Smalls.java
@@ -553,6 +553,88 @@ public class Smalls {
     }
   }
 
+  /** Example of a user-defined aggregate function (UDAF). */
+  public static class MyTwoParamsSumFunctionFilter1 {
+    public MyTwoParamsSumFunctionFilter1() {
+    }
+    public int init() {
+      return 0;
+    }
+    public int add(int accumulator, int v1, int v2) {
+      if (v1 > v2) {
+        return accumulator + v1;
+      }
+      return accumulator;
+    }
+    public int merge(int accumulator0, int accumulator1) {
+      return accumulator0 + accumulator1;
+    }
+    public int result(int accumulator) {
+      return accumulator;
+    }
+  }
+
+  /** Example of a user-defined aggregate function (UDAF). */
+  public static class MyTwoParamsSumFunctionFilter2 {
+    public MyTwoParamsSumFunctionFilter2() {
+    }
+    public long init() {
+      return 0L;
+    }
+    public long add(long accumulator, int v1, String v2) {
+      if (v2.equals("Eric")) {
+        return accumulator + v1;
+      }
+      return accumulator;
+    }
+    public long merge(long accumulator0, long accumulator1) {
+      return accumulator0 + accumulator1;
+    }
+    public long result(long accumulator) {
+      return accumulator;
+    }
+  }
+
+  /** Example of a user-defined aggregate function (UDAF), whose methods are
+   * static. */
+  public static class MyThreeParamsSumFunctionWithFilter1 {
+    public static long init() {
+      return 0L;
+    }
+    public static long add(long accumulator, int v1, String v2, String v3) {
+      if (v2.equals(v3)) {
+        return accumulator + v1;
+      }
+      return accumulator;
+    }
+    public static long merge(long accumulator0, long accumulator1) {
+      return accumulator0 + accumulator1;
+    }
+    public static long result(long accumulator) {
+      return accumulator;
+    }
+  }
+
+  /** Example of a user-defined aggregate function (UDAF), whose methods are
+   * static. olny for validate to get exact function by calcite*/
+  public static class MyThreeParamsSumFunctionWithFilter2 {
+    public static long init() {
+      return 0L;
+    }
+    public static long add(long accumulator, int v1, int v2, int v3) {
+      if (v3 > 250) {
+        return accumulator + v1 + v2;
+      }
+      return accumulator;
+    }
+    public static long merge(long accumulator0, long accumulator1) {
+      return accumulator0 + accumulator1;
+    }
+    public static long result(long accumulator) {
+      return accumulator;
+    }
+  }
+
   /** User-defined function. */
   public static class SumFunctionBadIAdd {
     public long init() {