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

hyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git


View the commit online:
https://github.com/apache/calcite/commit/cacc82ce99258abee0fad636d800814a6dcb1b77

The following commit(s) were added to refs/heads/master by this push:
     new cacc82c  [CALCITE-3112] Support Window in RelToSqlConverter (Wenhui 
Tang)
cacc82c is described below

commit cacc82ce99258abee0fad636d800814a6dcb1b77
Author: wenhuitang <[email protected]>
AuthorDate: Tue Jun 11 21:11:45 2019 +0800

    [CALCITE-3112] Support Window in RelToSqlConverter (Wenhui Tang)
    
    Close #1271
---
 .../java/org/apache/calcite/rel/core/Window.java   |  2 +-
 .../calcite/rel/rel2sql/RelToSqlConverter.java     | 25 +++++++
 .../apache/calcite/rel/rel2sql/SqlImplementor.java | 50 +++++++++++++
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java | 86 ++++++++++++++++++++++
 4 files changed, 162 insertions(+), 1 deletion(-)

diff --git a/core/src/main/java/org/apache/calcite/rel/core/Window.java 
b/core/src/main/java/org/apache/calcite/rel/core/Window.java
index 67b77a0..cd57b3a 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Window.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Window.java
@@ -390,7 +390,7 @@ public abstract class Window extends SingleRel {
     }
 
     @Override public RexCall clone(RelDataType type, List<RexNode> operands) {
-      throw new UnsupportedOperationException();
+      return super.clone(type, operands);
     }
   }
 }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java 
b/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
index cf5cbc2..017b5fc 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
@@ -40,6 +40,7 @@ import org.apache.calcite.rel.core.TableScan;
 import org.apache.calcite.rel.core.Uncollect;
 import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rel.core.Values;
+import org.apache.calcite.rel.core.Window;
 import org.apache.calcite.rel.logical.LogicalProject;
 import org.apache.calcite.rel.logical.LogicalSort;
 import org.apache.calcite.rel.type.RelDataType;
@@ -251,6 +252,30 @@ public class RelToSqlConverter extends SqlImplementor
   }
 
   /** @see #dispatch */
+  public Result visit(Window e) {
+    Result x = visitChild(0, e.getInput());
+    Builder builder = x.builder(e);
+    RelNode input = e.getInput();
+    int inputFieldCount = input.getRowType().getFieldCount();
+    final List<SqlNode> rexOvers = new ArrayList<>();
+    for (Window.Group group: e.groups) {
+      rexOvers.addAll(builder.context.toSql(group, e.constants, 
inputFieldCount));
+    }
+    final List<SqlNode> selectList = new ArrayList<>();
+
+    for (RelDataTypeField field: input.getRowType().getFieldList()) {
+      addSelect(selectList, builder.context.field(field.getIndex()), 
e.getRowType());
+    }
+
+    for (SqlNode rexOver: rexOvers) {
+      addSelect(selectList, rexOver, e.getRowType());
+    }
+
+    builder.setSelect(new SqlNodeList(selectList, POS));
+    return builder.result();
+  }
+
+  /** @see #dispatch */
   public Result visit(Aggregate e) {
     return visitAggregate(e, e.getGroupSet().toList());
   }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java 
b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
index a2f273d..d2c1800 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
@@ -24,6 +24,7 @@ import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Window;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexCall;
@@ -38,6 +39,7 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexOver;
 import org.apache.calcite.rex.RexPatternFieldRef;
 import org.apache.calcite.rex.RexProgram;
+import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.rex.RexSubQuery;
 import org.apache.calcite.rex.RexWindow;
 import org.apache.calcite.rex.RexWindowBound;
@@ -729,6 +731,54 @@ public abstract class SqlImplementor {
       }
     }
 
+    public List<SqlNode> toSql(Window.Group group, ImmutableList<RexLiteral> 
constants,
+        int inputFieldCount) {
+      final List<SqlNode> rexOvers = new ArrayList<>();
+      final List<SqlNode> partitionKeys = new ArrayList<>();
+      final List<SqlNode> orderByKeys = new ArrayList<>();
+      for (int partition: group.keys) {
+        partitionKeys.add(this.field(partition));
+      }
+      for (RelFieldCollation collation: group.orderKeys.getFieldCollations()) {
+        this.addOrderItem(orderByKeys, collation);
+      }
+      SqlLiteral isRows = SqlLiteral.createBoolean(group.isRows, POS);
+      SqlNode lowerBound = null;
+      SqlNode upperBound = null;
+
+      final SqlLiteral allowPartial = null;
+
+      for (Window.RexWinAggCall winAggCall: group.aggCalls) {
+        SqlAggFunction aggFunction = (SqlAggFunction) winAggCall.getOperator();
+        final SqlWindow sqlWindow = SqlWindow.create(null, null,
+                new SqlNodeList(partitionKeys, POS), new 
SqlNodeList(orderByKeys, POS),
+                isRows, lowerBound, upperBound, allowPartial, POS);
+        if (aggFunction.allowsFraming()) {
+          lowerBound = createSqlWindowBound(group.lowerBound);
+          upperBound = createSqlWindowBound(group.upperBound);
+          sqlWindow.setLowerBound(lowerBound);
+          sqlWindow.setUpperBound(upperBound);
+        }
+
+        RexShuttle replaceConstants = new RexShuttle() {
+          @Override public RexNode visitInputRef(RexInputRef inputRef) {
+            int index = inputRef.getIndex();
+            RexNode ref;
+            if (index > inputFieldCount - 1) {
+              ref = constants.get(index - inputFieldCount);
+            } else {
+              ref = inputRef;
+            }
+            return ref;
+          }
+        };
+        RexCall aggCall = (RexCall) winAggCall.accept(replaceConstants);
+        List<SqlNode> operands = toSql(null, aggCall.operands);
+        rexOvers.add(createOverCall(aggFunction, operands, sqlWindow));
+      }
+      return rexOvers;
+    }
+
     protected Context getAliasContext(RexCorrelVariable variable) {
       throw new UnsupportedOperationException();
     }
diff --git 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index f5418a4..ea438d4 100644
--- 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -24,6 +24,7 @@ import org.apache.calcite.plan.hep.HepPlanner;
 import org.apache.calcite.plan.hep.HepProgramBuilder;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.rules.ProjectToWindowRule;
 import org.apache.calcite.rel.rules.PruneEmptyRules;
 import org.apache.calcite.rel.rules.UnionMergeRule;
 import org.apache.calcite.rel.type.RelDataType;
@@ -2034,6 +2035,91 @@ public class RelToSqlConverterTest {
     sql(query).ok(expected);
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-3112";>[CALCITE-3112]
+   * Support Window in RelToSqlConverter</a>. */
+  @Test public void testConvertWinodwToSql() {
+    String query0 = "SELECT row_number() over (order by \"hire_date\") FROM 
\"employee\"";
+    String expected0 = "SELECT ROW_NUMBER() OVER (ORDER BY \"hire_date\") AS 
\"$0\"\n"
+            + "FROM \"foodmart\".\"employee\"";
+
+    String query1 = "SELECT rank() over (order by \"hire_date\") FROM 
\"employee\"";
+    String expected1 = "SELECT RANK() OVER (ORDER BY \"hire_date\") AS 
\"$0\"\n"
+            + "FROM \"foodmart\".\"employee\"";
+
+    String query2 = "SELECT lead(\"employee_id\",1,'NA') over "
+            + "(partition by \"hire_date\" order by \"employee_id\")\n"
+            + "FROM \"employee\"";
+    String expected2 = "SELECT LEAD(\"employee_id\", 1, 'NA') OVER "
+            + "(PARTITION BY \"hire_date\" "
+            + "ORDER BY \"employee_id\") AS \"$0\"\n"
+            + "FROM \"foodmart\".\"employee\"";
+
+    String query3 = "SELECT lag(\"employee_id\",1,'NA') over "
+            + "(partition by \"hire_date\" order by \"employee_id\")\n"
+            + "FROM \"employee\"";
+    String expected3 = "SELECT LAG(\"employee_id\", 1, 'NA') OVER "
+            + "(PARTITION BY \"hire_date\" ORDER BY \"employee_id\") AS 
\"$0\"\n"
+            + "FROM \"foodmart\".\"employee\"";
+
+    String query4 = "SELECT lag(\"employee_id\",1,'NA') "
+            + "over (partition by \"hire_date\" order by \"employee_id\") as 
lag1, "
+            + "lag(\"employee_id\",1,'NA') "
+            + "over (partition by \"birth_date\" order by \"employee_id\") as 
lag2, "
+            + "count(*) over (partition by \"hire_date\" order by 
\"employee_id\") as count1, "
+            + "count(*) over (partition by \"birth_date\" order by 
\"employee_id\") as count2\n"
+            + "FROM \"employee\"";
+    String expected4 = "SELECT LAG(\"employee_id\", 1, 'NA') OVER "
+            + "(PARTITION BY \"hire_date\" ORDER BY \"employee_id\") AS 
\"$0\", "
+            + "LAG(\"employee_id\", 1, 'NA') OVER "
+            + "(PARTITION BY \"birth_date\" ORDER BY \"employee_id\") AS 
\"$1\", "
+            + "COUNT(*) OVER (PARTITION BY \"hire_date\" ORDER BY 
\"employee_id\" "
+            + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS \"$2\", "
+            + "COUNT(*) OVER (PARTITION BY \"birth_date\" ORDER BY 
\"employee_id\" "
+            + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS \"$3\"\n"
+            + "FROM \"foodmart\".\"employee\"";
+
+    String query5 = "SELECT lag(\"employee_id\",1,'NA') "
+            + "over (partition by \"hire_date\" order by \"employee_id\") as 
lag1, "
+            + "lag(\"employee_id\",1,'NA') "
+            + "over (partition by \"birth_date\" order by \"employee_id\") as 
lag2, "
+            + "max(sum(\"employee_id\")) over (partition by \"hire_date\" 
order by \"employee_id\") as count1, "
+            + "max(sum(\"employee_id\")) over (partition by \"birth_date\" 
order by \"employee_id\") as count2\n"
+            + "FROM \"employee\" group by \"employee_id\", \"hire_date\", 
\"birth_date\"";
+    String expected5 = "SELECT LAG(\"employee_id\", 1, 'NA') OVER "
+            + "(PARTITION BY \"hire_date\" ORDER BY \"employee_id\") AS 
\"$0\", "
+            + "LAG(\"employee_id\", 1, 'NA') OVER "
+            + "(PARTITION BY \"birth_date\" ORDER BY \"employee_id\") AS 
\"$1\", "
+            + "MAX(SUM(\"employee_id\")) OVER (PARTITION BY \"hire_date\" 
ORDER BY \"employee_id\" "
+            + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS \"$2\", "
+            + "MAX(SUM(\"employee_id\")) OVER (PARTITION BY \"birth_date\" 
ORDER BY \"employee_id\" "
+            + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS \"$3\"\n"
+            + "FROM \"foodmart\".\"employee\"\n"
+            + "GROUP BY \"employee_id\", \"hire_date\", \"birth_date\"";
+
+    String query6 = "SELECT lag(\"employee_id\",1,'NA') over "
+            + "(partition by \"hire_date\" order by \"employee_id\"), 
\"hire_date\"\n"
+            + "FROM \"employee\"\n"
+            + "group by \"hire_date\", \"employee_id\"";
+    String expected6 = "SELECT LAG(\"employee_id\", 1, 'NA') "
+            + "OVER (PARTITION BY \"hire_date\" ORDER BY \"employee_id\"), 
\"hire_date\"\n"
+            + "FROM \"foodmart\".\"employee\"\n"
+            + "GROUP BY \"hire_date\", \"employee_id\"";
+
+    HepProgramBuilder builder = new HepProgramBuilder();
+    builder.addRuleClass(ProjectToWindowRule.class);
+    HepPlanner hepPlanner = new HepPlanner(builder.build());
+    RuleSet rules = RuleSets.ofList(ProjectToWindowRule.PROJECT);
+
+    sql(query0).optimize(rules, hepPlanner).ok(expected0);
+    sql(query1).optimize(rules, hepPlanner).ok(expected1);
+    sql(query2).optimize(rules, hepPlanner).ok(expected2);
+    sql(query3).optimize(rules, hepPlanner).ok(expected3);
+    sql(query4).optimize(rules, hepPlanner).ok(expected4);
+    sql(query5).optimize(rules, hepPlanner).ok(expected5);
+    sql(query6).optimize(rules, hepPlanner).ok(expected6);
+  }
+
   @Test public void testRankFunctionForPrintingOfFrameBoundary() {
     String query = "SELECT rank() over (order by \"hire_date\") FROM 
\"employee\"";
     String expected = "SELECT RANK() OVER (ORDER BY \"hire_date\")\n"

Reply via email to