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 2015/09/04 09:35:06 UTC
[08/10] incubator-calcite git commit: [CALCITE-844] Push Project
through Window (Hsuan-Yi Chu)
[CALCITE-844] Push Project through Window (Hsuan-Yi Chu)
Close apache/incubator-calcite#123
Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/5219eb8c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/5219eb8c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/5219eb8c
Branch: refs/heads/master
Commit: 5219eb8cd4e8c1f0b1654da3136ce1c04e38a96e
Parents: 0a4fa37
Author: Hsuan-Yi Chu <hs...@usc.edu>
Authored: Fri Aug 21 23:20:15 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Sep 4 00:34:00 2015 -0700
----------------------------------------------------------------------
.../rel/rules/ProjectWindowTransposeRule.java | 245 +++++++++++++++++++
.../apache/calcite/test/RelOptRulesTest.java | 11 +
.../org/apache/calcite/test/RelOptRulesTest.xml | 20 ++
3 files changed, 276 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5219eb8c/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
new file mode 100644
index 0000000..2705420
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
@@ -0,0 +1,245 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.rel.RelCollations;
+import org.apache.calcite.rel.RelFieldCollation;
+import org.apache.calcite.rel.core.Window;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalWindow;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rel.type.RelRecordType;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
+import org.apache.calcite.sql.SqlAggFunction;
+import org.apache.calcite.util.ImmutableBitSet;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Planner rule that pushes
+ * a {@link org.apache.calcite.rel.logical.LogicalProject}
+ * past a {@link org.apache.calcite.rel.logical.LogicalWindow}.
+ */
+public class ProjectWindowTransposeRule extends RelOptRule {
+ /** The default instance of
+ * {@link org.apache.calcite.rel.rules.ProjectWindowTransposeRule}. */
+ public static final ProjectWindowTransposeRule INSTANCE = new ProjectWindowTransposeRule();
+
+ private ProjectWindowTransposeRule() {
+ super(
+ operand(
+ LogicalProject.class,
+ operand(LogicalWindow.class,
+ any())));
+ }
+
+ @Override
+ public void onMatch(RelOptRuleCall call) {
+ final LogicalProject project = call.rel(0);
+ final LogicalWindow window = call.rel(1);
+ final List<RelDataTypeField> rowTypeWindowInput = window.getInput().getRowType().getFieldList();
+ final int windowInputColumn = rowTypeWindowInput.size();
+
+ // Record the window input columns which are actually referred
+ // either in the LogicalProject above LogicalWindow or LogicalWindow itself
+ final BitSet beReferred = findReference(project, window);
+
+ // If all the the window input columns are referred,
+ // it is impossible to trim anyone of them out
+ if (beReferred.cardinality() == windowInputColumn) {
+ return;
+ }
+
+ // Put a DrillProjectRel below LogicalWindow
+ final List<RexNode> exps = new ArrayList<>();
+ final List<RelDataTypeField> fields = new ArrayList<>();
+
+ // Keep only the fields which are referred
+ for (int index = beReferred.nextSetBit(0); index >= 0;
+ index = beReferred.nextSetBit(index + 1)) {
+ final RelDataTypeField relDataTypeField = rowTypeWindowInput.get(index);
+ exps.add(new RexInputRef(index, relDataTypeField.getType()));
+ fields.add(relDataTypeField);
+ }
+
+ final LogicalProject projectBelowWindow = new LogicalProject(
+ window.getCluster(),
+ window.getTraitSet(),
+ window.getInput(),
+ exps,
+ new RelRecordType(fields));
+
+ // Create a new LogicalWindow with necessary inputs only
+ final List<RelDataTypeField> inputWinFields = fields;
+ final List<RelDataTypeField> outputWindFields = new ArrayList<>(inputWinFields);
+ final List<Window.Group> groups = new ArrayList<>();
+
+ // As the un-referred columns are trimmed by the LogicalProject,
+ // the indices specified in LogicalWindow would need to be adjusted
+ final RexShuttle indexAdjustment = new RexShuttle() {
+ @Override
+ public RexNode visitInputRef(RexInputRef inputRef) {
+ final int index = inputRef.getIndex();
+ final int newIndex = beReferred.get(0, index).cardinality();
+ return new RexInputRef(newIndex, inputRef.getType());
+ }
+
+ @Override
+ public RexNode visitCall(final RexCall call) {
+ final List<RexNode> clonedOperands = visitList(call.operands, new boolean[]{false});
+ if (call instanceof Window.RexWinAggCall) {
+ return new Window.RexWinAggCall(
+ (SqlAggFunction) call.getOperator(),
+ call.getType(),
+ clonedOperands,
+ ((Window.RexWinAggCall) call).ordinal);
+ } else {
+ return call;
+ }
+ }
+ };
+
+ int aggCallIndex = windowInputColumn;
+ for (Window.Group group : window.groups) {
+ final ImmutableBitSet.Builder keys = ImmutableBitSet.builder();
+ final List<RelFieldCollation> orderKeys = new ArrayList<>();
+ final List<Window.RexWinAggCall> aggCalls = new ArrayList<>();
+
+ // Adjust keys
+ for (int index : group.keys) {
+ keys.set(beReferred.get(0, index).cardinality());
+ }
+
+ // Adjust orderKeys
+ for (RelFieldCollation relFieldCollation : group.orderKeys.getFieldCollations()) {
+ final int index = relFieldCollation.getFieldIndex();
+ orderKeys.add(relFieldCollation.copy(beReferred.get(0, index).cardinality()));
+ }
+
+ // Adjust Window Functions
+ for (Window.RexWinAggCall rexWinAggCall : group.aggCalls) {
+ aggCalls.add((Window.RexWinAggCall) rexWinAggCall.accept(indexAdjustment));
+
+ final RelDataTypeField relDataTypeField =
+ window.getRowType().getFieldList().get(aggCallIndex);
+ outputWindFields.add(relDataTypeField);
+ ++aggCallIndex;
+ }
+
+ groups.add(new Window.Group(
+ keys.build(),
+ group.isRows,
+ group.lowerBound,
+ group.upperBound,
+ RelCollations.of(orderKeys),
+ aggCalls));
+ }
+
+ final LogicalWindow newLogicalWindow = new LogicalWindow(
+ window.getCluster(),
+ window.getTraitSet(),
+ projectBelowWindow,
+ window.constants,
+ new RelRecordType(outputWindFields),
+ groups);
+
+ // Modify the top LogicalProject
+ final List<RexNode> topProjExps = new ArrayList<>();
+ final RexShuttle topProjAdjustment = new RexShuttle() {
+ @Override
+ public RexNode visitInputRef(RexInputRef inputRef) {
+ final int index = inputRef.getIndex();
+ final int newIndex;
+ if (index >= windowInputColumn) {
+ newIndex = beReferred.cardinality() + (index - windowInputColumn);
+ } else {
+ newIndex = beReferred.get(0, index).cardinality();
+ }
+ return new RexInputRef(newIndex, inputRef.getType());
+ }
+ };
+
+ for (RexNode rexNode : project.getChildExps()) {
+ topProjExps.add(rexNode.accept(topProjAdjustment));
+ }
+
+ final LogicalProject newTopProj = project.copy(
+ newLogicalWindow.getTraitSet(),
+ newLogicalWindow,
+ topProjExps,
+ project.getRowType());
+
+ if (ProjectRemoveRule.isTrivial(newTopProj)) {
+ call.transformTo(newLogicalWindow);
+ } else {
+ call.transformTo(newTopProj);
+ }
+ }
+
+ private BitSet findReference(final LogicalProject project, final LogicalWindow window) {
+ final int windowInputColumn = window.getInput().getRowType().getFieldCount();
+ final BitSet beReferred = new BitSet(window.getInput().getRowType().getFieldCount());
+
+ final RexShuttle referenceFinder = new RexShuttle() {
+ @Override
+ public RexNode visitInputRef(RexInputRef inputRef) {
+ final int index = inputRef.getIndex();
+ if (index < windowInputColumn) {
+ beReferred.set(index);
+ }
+ return inputRef;
+ }
+ };
+
+ // Reference in LogicalProject
+ for (RexNode rexNode : project.getChildExps()) {
+ rexNode.accept(referenceFinder);
+ }
+
+ // Reference in LogicalWindow
+ for (Window.Group group : window.groups) {
+ // Reference in Partition-By
+ for (int index : group.keys) {
+ if (index < windowInputColumn) {
+ beReferred.set(index);
+ }
+ }
+
+ // Reference in Order-By
+ for (RelFieldCollation relFieldCollation : group.orderKeys.getFieldCollations()) {
+ if (relFieldCollation.getFieldIndex() < windowInputColumn) {
+ beReferred.set(relFieldCollation.getFieldIndex());
+ }
+ }
+
+ // Reference in Window Functions
+ for (Window.RexWinAggCall rexWinAggCall : group.aggCalls) {
+ rexWinAggCall.accept(referenceFinder);
+ }
+ }
+ return beReferred;
+ }
+}
+
+// End ProjectWindowTransposeRule.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5219eb8c/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index bdea4af..e34b1bb 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -64,6 +64,7 @@ import org.apache.calcite.rel.rules.ProjectRemoveRule;
import org.apache.calcite.rel.rules.ProjectSetOpTransposeRule;
import org.apache.calcite.rel.rules.ProjectToCalcRule;
import org.apache.calcite.rel.rules.ProjectToWindowRule;
+import org.apache.calcite.rel.rules.ProjectWindowTransposeRule;
import org.apache.calcite.rel.rules.PruneEmptyRules;
import org.apache.calcite.rel.rules.ReduceExpressionsRule;
import org.apache.calcite.rel.rules.SemiJoinFilterTransposeRule;
@@ -1609,6 +1610,16 @@ public class RelOptRulesTest extends RelOptTestBase {
FilterProjectTransposeRule.INSTANCE);
}
+ @Test public void testProjectWindowTransposeRule() {
+ HepProgram program = new HepProgramBuilder()
+ .addRuleInstance(ProjectToWindowRule.PROJECT)
+ .addRuleInstance(ProjectWindowTransposeRule.INSTANCE)
+ .build();
+
+ final String sql = "select count(empno) over(), deptno from emp";
+ checkPlanning(program, sql);
+ }
+
@Test public void testPushFilterWithRank() throws Exception {
HepProgram program = new HepProgramBuilder().addRuleInstance(
FilterProjectTransposeRule.INSTANCE).build();
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5219eb8c/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 4ab7a81..7b40b5d 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -3041,6 +3041,26 @@ LogicalAggregate(group=[{0, 1}])
]]>
</Resource>
</TestCase>
+ <TestCase name="testProjectWindowTransposeRule">
+ <Resource name="sql">
+ <![CDATA[select count(empno) over(), deptno from emp]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(EXPR$0=[$1], DEPTNO=[$0])
+ LogicalProject(DEPTNO=[$1], $1=[$2])
+ LogicalWindow(window#0=[window(partition {} order by [] range between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING aggs [COUNT($0)])])
+ LogicalProject(EMPNO=[$0], DEPTNO=[$7])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(EXPR$0=[COUNT($0) OVER (RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)], DEPTNO=[$7])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
<TestCase name="testPushFilterWithRank">
<Resource name="sql">
<![CDATA[select e1.ename, r