[CALCITE-2206] JDBC adapter incorrectly pushes windowed aggregates down to 
HSQLDB (Pavel Gubin)

Close apache/calcite#646


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

Branch: refs/heads/master
Commit: 502c108473e535a76cebc5e09bd6be5efcc24531
Parents: 000622d
Author: pavelgubin <pa...@contiamo.com>
Authored: Wed Mar 7 14:10:54 2018 +0100
Committer: Julian Hyde <jh...@apache.org>
Committed: Tue Apr 10 23:26:19 2018 -0700

----------------------------------------------------------------------
 .../apache/calcite/adapter/jdbc/JdbcRules.java  | 152 +++++++++++++++----
 .../java/org/apache/calcite/sql/SqlDialect.java |   5 +
 .../calcite/sql/dialect/AccessSqlDialect.java   |   4 +
 .../calcite/sql/dialect/H2SqlDialect.java       |   4 +
 .../calcite/sql/dialect/HsqldbSqlDialect.java   |   4 +
 .../sql/dialect/InfobrightSqlDialect.java       |   5 +
 .../apache/calcite/test/JdbcAdapterTest.java    |  24 +++
 7 files changed, 165 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/502c1084/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java 
b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
index 8c07fef..dd0c461 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
@@ -58,14 +58,19 @@ import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexMultisetUtil;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexOver;
 import org.apache.calcite.rex.RexProgram;
+import org.apache.calcite.runtime.PredicateImpl;
 import org.apache.calcite.schema.ModifiableTable;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.tools.RelBuilderFactory;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Util;
 import org.apache.calcite.util.trace.CalciteTrace;
 
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
 
 import org.slf4j.Logger;
@@ -73,6 +78,7 @@ import org.slf4j.Logger;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import javax.annotation.Nullable;
 
 /**
  * Rules and relational operators for
@@ -86,37 +92,57 @@ public class JdbcRules {
   protected static final Logger LOGGER = CalciteTrace.getPlannerTracer();
 
   public static List<RelOptRule> rules(JdbcConvention out) {
+    return rules(out, RelFactories.LOGICAL_BUILDER);
+  }
+
+  public static List<RelOptRule> rules(JdbcConvention out,
+      RelBuilderFactory relBuilderFactory) {
     return ImmutableList.<RelOptRule>of(
-        new JdbcToEnumerableConverterRule(out, RelFactories.LOGICAL_BUILDER),
-        new JdbcJoinRule(out),
-        new JdbcCalcRule(out),
-        new JdbcProjectRule(out),
-        new JdbcFilterRule(out),
-        new JdbcAggregateRule(out),
-        new JdbcSortRule(out),
-        new JdbcUnionRule(out),
-        new JdbcIntersectRule(out),
-        new JdbcMinusRule(out),
-        new JdbcTableModificationRule(out),
-        new JdbcValuesRule(out));
+        new JdbcToEnumerableConverterRule(out, relBuilderFactory),
+        new JdbcJoinRule(out, relBuilderFactory),
+        new JdbcCalcRule(out, relBuilderFactory),
+        new JdbcProjectRule(out, relBuilderFactory),
+        new JdbcFilterRule(out, relBuilderFactory),
+        new JdbcAggregateRule(out, relBuilderFactory),
+        new JdbcSortRule(out, relBuilderFactory),
+        new JdbcUnionRule(out, relBuilderFactory),
+        new JdbcIntersectRule(out, relBuilderFactory),
+        new JdbcMinusRule(out, relBuilderFactory),
+        new JdbcTableModificationRule(out, relBuilderFactory),
+        new JdbcValuesRule(out, relBuilderFactory));
   }
 
   /** Abstract base class for rule that converts to JDBC. */
   abstract static class JdbcConverterRule extends ConverterRule {
     protected final JdbcConvention out;
 
+    @Deprecated // to be removed before 2.0
     JdbcConverterRule(Class<? extends RelNode> clazz, RelTrait in,
         JdbcConvention out, String description) {
-      super(clazz, in, out, description);
+      this(clazz, Predicates.<RelNode>alwaysTrue(), in, out,
+          RelFactories.LOGICAL_BUILDER, description);
+    }
+
+    <R extends RelNode> JdbcConverterRule(Class<R> clazz,
+        Predicate<? super R> predicate, RelTrait in, JdbcConvention out,
+        RelBuilderFactory relBuilderFactory, String description) {
+      super(clazz, predicate, in, out, relBuilderFactory, description);
       this.out = out;
     }
   }
 
   /** Rule that converts a join to JDBC. */
   public static class JdbcJoinRule extends JdbcConverterRule {
-    /** Creates a JdbcJoinRule. */
+    @Deprecated // to be removed before 2.0
     public JdbcJoinRule(JdbcConvention out) {
-      super(Join.class, Convention.NONE, out, "JdbcJoinRule");
+      this(out, RelFactories.LOGICAL_BUILDER);
+    }
+
+    /** Creates a JdbcJoinRule. */
+    public JdbcJoinRule(JdbcConvention out,
+        RelBuilderFactory relBuilderFactory) {
+      super(Join.class, Predicates.<RelNode>alwaysTrue(), Convention.NONE,
+          out, relBuilderFactory, "JdbcJoinRule");
     }
 
     @Override public RelNode convert(RelNode rel) {
@@ -267,8 +293,11 @@ public class JdbcRules {
    * {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcCalc}.
    */
   private static class JdbcCalcRule extends JdbcConverterRule {
-    private JdbcCalcRule(JdbcConvention out) {
-      super(Calc.class, Convention.NONE, out, "JdbcCalcRule");
+    /** Creates a JdbcCalcRule. */
+    private JdbcCalcRule(JdbcConvention out,
+        RelBuilderFactory relBuilderFactory) {
+      super(Calc.class, Predicates.<RelNode>alwaysTrue(), Convention.NONE,
+          out, relBuilderFactory, "JdbcCalcRule");
     }
 
     public RelNode convert(RelNode rel) {
@@ -340,8 +369,23 @@ public class JdbcRules {
    * an {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcProject}.
    */
   public static class JdbcProjectRule extends JdbcConverterRule {
-    public JdbcProjectRule(JdbcConvention out) {
-      super(Project.class, Convention.NONE, out, "JdbcProjectRule");
+    @Deprecated // to be removed before 2.0
+    public JdbcProjectRule(final JdbcConvention out) {
+      this(out, RelFactories.LOGICAL_BUILDER);
+    }
+
+    /** Creates a JdbcProjectRule. */
+    public JdbcProjectRule(final JdbcConvention out,
+        RelBuilderFactory relBuilderFactory) {
+      super(Project.class,
+          new PredicateImpl<Project>() {
+            public boolean test(@Nullable Project project) {
+              assert project != null;
+              return out.dialect.supportsWindowFunctions()
+                  && !RexOver.containsOver(project.getProjects(), null);
+            }
+          },
+          Convention.NONE, out, relBuilderFactory, "JdbcProjectRule");
     }
 
     public RelNode convert(RelNode rel) {
@@ -401,8 +445,16 @@ public class JdbcRules {
    * an {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcFilter}.
    */
   public static class JdbcFilterRule extends JdbcConverterRule {
+    @Deprecated // to be removed before 2.0
     public JdbcFilterRule(JdbcConvention out) {
-      super(Filter.class, Convention.NONE, out, "JdbcFilterRule");
+      this(out, RelFactories.LOGICAL_BUILDER);
+    }
+
+    /** Creates a JdbcFilterRule. */
+    public JdbcFilterRule(JdbcConvention out,
+        RelBuilderFactory relBuilderFactory) {
+      super(Filter.class, Predicates.<RelNode>alwaysTrue(), Convention.NONE,
+          out, relBuilderFactory, "JdbcFilterRule");
     }
 
     public RelNode convert(RelNode rel) {
@@ -444,8 +496,16 @@ public class JdbcRules {
    * to a {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcAggregate}.
    */
   public static class JdbcAggregateRule extends JdbcConverterRule {
+    @Deprecated // to be removed before 2.0
     public JdbcAggregateRule(JdbcConvention out) {
-      super(Aggregate.class, Convention.NONE, out, "JdbcAggregateRule");
+      this(out, RelFactories.LOGICAL_BUILDER);
+    }
+
+    /** Creates a JdbcAggregateRule. */
+    public JdbcAggregateRule(JdbcConvention out,
+        RelBuilderFactory relBuilderFactory) {
+      super(Aggregate.class, Predicates.<RelNode>alwaysTrue(), Convention.NONE,
+          out, relBuilderFactory, "JdbcAggregateRule");
     }
 
     public RelNode convert(RelNode rel) {
@@ -521,9 +581,16 @@ public class JdbcRules {
    * {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcSort}.
    */
   public static class JdbcSortRule extends JdbcConverterRule {
-    /** Creates a JdbcSortRule. */
+    @Deprecated // to be removed before 2.0
     public JdbcSortRule(JdbcConvention out) {
-      super(Sort.class, Convention.NONE, out, "JdbcSortRule");
+      this(out, RelFactories.LOGICAL_BUILDER);
+    }
+
+    /** Creates a JdbcSortRule. */
+    public JdbcSortRule(JdbcConvention out,
+        RelBuilderFactory relBuilderFactory) {
+      super(Sort.class, Predicates.<RelNode>alwaysTrue(), Convention.NONE, out,
+          relBuilderFactory, "JdbcSortRule");
     }
 
     public RelNode convert(RelNode rel) {
@@ -585,8 +652,16 @@ public class JdbcRules {
    * {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcUnion}.
    */
   public static class JdbcUnionRule extends JdbcConverterRule {
+    @Deprecated // to be removed before 2.0
     public JdbcUnionRule(JdbcConvention out) {
-      super(Union.class, Convention.NONE, out, "JdbcUnionRule");
+      this(out, RelFactories.LOGICAL_BUILDER);
+    }
+
+    /** Creates a JdbcUnionRule. */
+    public JdbcUnionRule(JdbcConvention out,
+        RelBuilderFactory relBuilderFactory) {
+      super(Union.class, Predicates.<RelNode>alwaysTrue(), Convention.NONE, 
out,
+          relBuilderFactory, "JdbcUnionRule");
     }
 
     public RelNode convert(RelNode rel) {
@@ -628,8 +703,11 @@ public class JdbcRules {
    * to a {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcIntersect}.
    */
   public static class JdbcIntersectRule extends JdbcConverterRule {
-    private JdbcIntersectRule(JdbcConvention out) {
-      super(Intersect.class, Convention.NONE, out, "JdbcIntersectRule");
+    /** Creates a JdbcIntersectRule. */
+    private JdbcIntersectRule(JdbcConvention out,
+        RelBuilderFactory relBuilderFactory) {
+      super(Intersect.class, Predicates.<RelNode>alwaysTrue(), Convention.NONE,
+          out, relBuilderFactory, "JdbcIntersectRule");
     }
 
     public RelNode convert(RelNode rel) {
@@ -672,8 +750,11 @@ public class JdbcRules {
    * {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcMinus}.
    */
   public static class JdbcMinusRule extends JdbcConverterRule {
-    private JdbcMinusRule(JdbcConvention out) {
-      super(Minus.class, Convention.NONE, out, "JdbcMinusRule");
+    /** Creates a JdbcMinusRule. */
+    private JdbcMinusRule(JdbcConvention out,
+        RelBuilderFactory relBuilderFactory) {
+      super(Minus.class, Predicates.<RelNode>alwaysTrue(), Convention.NONE, 
out,
+          relBuilderFactory, "JdbcMinusRule");
     }
 
     public RelNode convert(RelNode rel) {
@@ -708,9 +789,11 @@ public class JdbcRules {
 
   /** Rule that converts a table-modification to JDBC. */
   public static class JdbcTableModificationRule extends JdbcConverterRule {
-    private JdbcTableModificationRule(JdbcConvention out) {
-      super(TableModify.class, Convention.NONE, out,
-          "JdbcTableModificationRule");
+    /** Creates a JdbcTableModificationRule. */
+    private JdbcTableModificationRule(JdbcConvention out,
+        RelBuilderFactory relBuilderFactory) {
+      super(TableModify.class, Predicates.<RelNode>alwaysTrue(),
+          Convention.NONE, out, relBuilderFactory, 
"JdbcTableModificationRule");
     }
 
     @Override public RelNode convert(RelNode rel) {
@@ -782,8 +865,11 @@ public class JdbcRules {
 
   /** Rule that converts a values operator to JDBC. */
   public static class JdbcValuesRule extends JdbcConverterRule {
-    private JdbcValuesRule(JdbcConvention out) {
-      super(Values.class, Convention.NONE, out, "JdbcValuesRule");
+    /** Creates a JdbcValuesRule. */
+    private JdbcValuesRule(JdbcConvention out,
+        RelBuilderFactory relBuilderFactory) {
+      super(Values.class, Predicates.<RelNode>alwaysTrue(), Convention.NONE,
+          out, relBuilderFactory, "JdbcValuesRule");
     }
 
     @Override public RelNode convert(RelNode rel) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/502c1084/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
index c0ebbb9..3e679d3 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
@@ -582,6 +582,11 @@ public class SqlDialect {
     return false;
   }
 
+  /** Returns whether this dialect supports window functions (OVER clause). */
+  public boolean supportsWindowFunctions() {
+    return true;
+  }
+
   /** Returns whether this dialect supports a given function or operator. */
   public boolean supportsFunction(SqlOperator operator, RelDataType type,
       List<RelDataType> paramTypes) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/502c1084/core/src/main/java/org/apache/calcite/sql/dialect/AccessSqlDialect.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/AccessSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/AccessSqlDialect.java
index 1c2861f..91d69e7 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/AccessSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/AccessSqlDialect.java
@@ -31,6 +31,10 @@ public class AccessSqlDialect extends SqlDialect {
   public AccessSqlDialect(Context context) {
     super(context);
   }
+
+  @Override public boolean supportsWindowFunctions() {
+    return false;
+  }
 }
 
 // End AccessSqlDialect.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/502c1084/core/src/main/java/org/apache/calcite/sql/dialect/H2SqlDialect.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/H2SqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/H2SqlDialect.java
index 005482a..a30c3f0 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/H2SqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/H2SqlDialect.java
@@ -35,6 +35,10 @@ public class H2SqlDialect extends SqlDialect {
   @Override public boolean supportsCharSet() {
     return false;
   }
+
+  @Override public boolean supportsWindowFunctions() {
+    return false;
+  }
 }
 
 // End H2SqlDialect.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/502c1084/core/src/main/java/org/apache/calcite/sql/dialect/HsqldbSqlDialect.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/HsqldbSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/HsqldbSqlDialect.java
index 70dd55d..1cbd56c 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/HsqldbSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/HsqldbSqlDialect.java
@@ -46,6 +46,10 @@ public class HsqldbSqlDialect extends SqlDialect {
     return false;
   }
 
+  @Override public boolean supportsWindowFunctions() {
+    return false;
+  }
+
   @Override public void unparseCall(SqlWriter writer, SqlCall call,
       int leftPrec, int rightPrec) {
     switch (call.getKind()) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/502c1084/core/src/main/java/org/apache/calcite/sql/dialect/InfobrightSqlDialect.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/InfobrightSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/InfobrightSqlDialect.java
index 78ba060..80f8baf 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/dialect/InfobrightSqlDialect.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/dialect/InfobrightSqlDialect.java
@@ -31,6 +31,11 @@ public class InfobrightSqlDialect extends SqlDialect {
   public InfobrightSqlDialect(Context context) {
     super(context);
   }
+
+  @Override public boolean supportsWindowFunctions() {
+    return false;
+  }
+
 }
 
 // End InfobrightSqlDialect.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/502c1084/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java 
b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
index c35ef8a..a4db644 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
@@ -419,6 +419,30 @@ public class JdbcAdapterTest {
   }
 
   /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-2206";>[CALCITE-2206]
+   * JDBC adapter incorrectly pushes windowed aggregates down to HSQLDB</a>. */
+  @Test public void testOverNonSupportedDialect() {
+    final String sql = "select \"store_id\", \"account_id\", \"exp_date\",\n"
+        + " \"time_id\", \"category_id\", \"currency_id\", \"amount\",\n"
+        + " last_value(\"time_id\") over () as \"last_version\"\n"
+        + "from \"expense_fact\"";
+    final String explain = "PLAN="
+        + "EnumerableWindow(window#0=[window(partition {} "
+        + "order by [] range between UNBOUNDED PRECEDING and "
+        + "UNBOUNDED FOLLOWING aggs [LAST_VALUE($3)])])\n"
+        + "  JdbcToEnumerableConverter\n"
+        + "    JdbcTableScan(table=[[foodmart, expense_fact]])\n";
+    CalciteAssert
+        .model(JdbcTest.FOODMART_MODEL)
+        .enable(CalciteAssert.DB == DatabaseInstance.HSQLDB)
+        .query(sql)
+        .explainContains(explain)
+        .runs()
+        .planHasSql("SELECT *\n"
+            + "FROM \"foodmart\".\"expense_fact\"");
+  }
+
+  /** Test case for
    * <a 
href="https://issues.apache.org/jira/browse/CALCITE-1506";>[CALCITE-1506]
    * Push OVER Clause to underlying SQL via JDBC adapter</a>.
    *

Reply via email to