You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by bu...@apache.org on 2017/08/18 00:28:55 UTC

asterixdb git commit: [ASTERIXDB-2044][COMP] Eliminate listify for complex group-by

Repository: asterixdb
Updated Branches:
  refs/heads/master fcd89f5a9 -> 5170fb212


[ASTERIXDB-2044][COMP] Eliminate listify for complex group-by

- user model changes: no
- storage format changes: no
- interface changes: no

Details:
- Fix EliminateSubplanWithInputCardinalityOneRule to handle recursive
  subplans;
- Fix various places that assumes the nested plans inside a group-by
  operator cannot be empty;
- Added regression tests.

Change-Id: Ida9aa8d89a89f90256e54c8c1806af9b4a162d21
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1946
Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>
Contrib: Jenkins <je...@fulliautomatix.ics.uci.edu>
Reviewed-by: Yingyi Bu <bu...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/asterixdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/asterixdb/commit/5170fb21
Tree: http://git-wip-us.apache.org/repos/asf/asterixdb/tree/5170fb21
Diff: http://git-wip-us.apache.org/repos/asf/asterixdb/diff/5170fb21

Branch: refs/heads/master
Commit: 5170fb212aa0e2c1e93327553fc38b6acd22c73b
Parents: fcd89f5
Author: Yingyi Bu <yi...@couchbase.com>
Authored: Thu Aug 17 12:50:55 2017 -0700
Committer: Yingyi Bu <bu...@gmail.com>
Committed: Thu Aug 17 17:28:35 2017 -0700

----------------------------------------------------------------------
 .../queries/query-ASTERIXDB-810-2.sqlpp         | 73 ++++++++++++++++++++
 .../queries/query-ASTERIXDB-810.sqlpp           | 73 ++++++++++++++++++++
 .../results/query-ASTERIXDB-810-2.plan          | 37 ++++++++++
 .../results/query-ASTERIXDB-810.plan            | 39 +++++++++++
 .../results/udfs/query-ASTERIXDB-1308-1.plan    | 13 ++--
 .../physical/PreclusteredGroupByPOperator.java  |  4 +-
 .../algebra/typing/AbstractTypeEnvironment.java |  3 +-
 .../AbstractIntroduceGroupByCombinerRule.java   |  5 ++
 ...hNestedOrderByUnderPreSortedGroupByRule.java |  3 +
 ...inateSubplanWithInputCardinalityOneRule.java | 71 ++++++++++---------
 10 files changed, 276 insertions(+), 45 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5170fb21/asterixdb/asterix-app/src/test/resources/optimizerts/queries/query-ASTERIXDB-810-2.sqlpp
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/query-ASTERIXDB-810-2.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/query-ASTERIXDB-810-2.sqlpp
new file mode 100644
index 0000000..b6f6159
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/query-ASTERIXDB-810-2.sqlpp
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+/*
+ * Description  : This test case is to verify the fix for issue810
+ * https://code.google.com/p/asterixdb/issues/detail?id=810
+ * Expected Res : SUCCESS
+ * Date         : 16th Nov. 2014
+ */
+
+DROP  DATAVERSE tpch IF EXISTS;
+CREATE  dataverse tpch;
+
+USE tpch;
+
+
+CREATE TYPE LineItemType AS CLOSED {
+  l_orderkey : integer,
+  l_partkey : integer,
+  l_suppkey : integer,
+  l_linenumber : integer,
+  l_quantity : double,
+  l_extendedprice : double,
+  l_discount : double,
+  l_tax : double,
+  l_returnflag : string,
+  l_linestatus : string,
+  l_shipdate : string,
+  l_commitdate : string,
+  l_receiptdate : string,
+  l_shipinstruct : string,
+  l_shipmode : string,
+  l_comment : string
+}
+
+CREATE DATASET LineItem(LineItemType) PRIMARY KEY l_orderkey,l_linenumber;
+
+
+SELECT l_returnflag AS l_returnflag,
+       l_linestatus AS l_linestatus,
+       coll_count(cheap) AS count_cheaps,
+       coll_count(expensive) AS count_expensives
+FROM LineItem AS l
+/* +hash */
+GROUP BY l.l_returnflag AS l_returnflag,l.l_linestatus AS l_linestatus
+GROUP AS g
+LET cheap = (
+      SELECT ELEMENT g.l
+      FROM g
+      WHERE g.l.l_discount > 0.05
+),
+expensive = (
+      SELECT ELEMENT g.l
+      FROM g
+      WHERE g.l.l_discount <= 0.05
+)
+ORDER BY l_returnflag,l_linestatus
+;

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5170fb21/asterixdb/asterix-app/src/test/resources/optimizerts/queries/query-ASTERIXDB-810.sqlpp
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/query-ASTERIXDB-810.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/query-ASTERIXDB-810.sqlpp
new file mode 100644
index 0000000..451f163
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/query-ASTERIXDB-810.sqlpp
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+/*
+ * Description  : This test case is to verify the fix for issue810
+ * https://code.google.com/p/asterixdb/issues/detail?id=810
+ * Expected Res : SUCCESS
+ * Date         : 16th Nov. 2014
+ */
+
+DROP  DATAVERSE tpch IF EXISTS;
+CREATE  dataverse tpch;
+
+USE tpch;
+
+
+CREATE TYPE LineItemType AS CLOSED {
+  l_orderkey : integer,
+  l_partkey : integer,
+  l_suppkey : integer,
+  l_linenumber : integer,
+  l_quantity : double,
+  l_extendedprice : double,
+  l_discount : double,
+  l_tax : double,
+  l_returnflag : string,
+  l_linestatus : string,
+  l_shipdate : string,
+  l_commitdate : string,
+  l_receiptdate : string,
+  l_shipinstruct : string,
+  l_shipmode : string,
+  l_comment : string
+}
+
+CREATE DATASET LineItem(LineItemType) PRIMARY KEY l_orderkey,l_linenumber;
+
+
+SELECT l_returnflag AS l_returnflag,
+       l_linestatus AS l_linestatus,
+       coll_count(cheap) AS count_cheaps,
+       coll_count(expensive) AS count_expensives
+FROM LineItem AS l
+/* +hash */
+GROUP BY l.l_returnflag AS l_returnflag,l.l_linestatus AS l_linestatus
+GROUP AS g
+LET cheap = (
+      SELECT ELEMENT m
+      FROM (FROM g SELECT VALUE l) AS m
+      WHERE m.l_discount > 0.05
+),
+expensive = (
+      SELECT ELEMENT m
+      FROM (FROM g SELECT VALUE l) AS m
+      WHERE m.l_discount <= 0.05
+)
+ORDER BY l_returnflag,l_linestatus
+;

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5170fb21/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-810-2.plan
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-810-2.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-810-2.plan
new file mode 100644
index 0000000..cad2fbb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-810-2.plan
@@ -0,0 +1,37 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- SORT_MERGE_EXCHANGE [$$l_returnflag(ASC), $$l_linestatus(ASC) ]  |PARTITIONED|
+          -- PRE_CLUSTERED_GROUP_BY[$$42, $$43]  |PARTITIONED|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              -- STABLE_SORT [$$42(ASC), $$43(ASC)]  |PARTITIONED|
+                -- HASH_PARTITION_EXCHANGE [$$42, $$43]  |PARTITIONED|
+                  -- PRE_CLUSTERED_GROUP_BY[$$30, $$31]  |PARTITIONED|
+                          {
+                            -- AGGREGATE  |LOCAL|
+                              -- STREAM_SELECT  |LOCAL|
+                                -- NESTED_TUPLE_SOURCE  |LOCAL|
+                          }
+                          {
+                            -- AGGREGATE  |LOCAL|
+                              -- STREAM_SELECT  |LOCAL|
+                                -- NESTED_TUPLE_SOURCE  |LOCAL|
+                          }
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- STABLE_SORT [$$30(ASC), $$31(ASC)]  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- ASSIGN  |PARTITIONED|
+                            -- STREAM_PROJECT  |PARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                -- DATASOURCE_SCAN  |PARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    -- EMPTY_TUPLE_SOURCE  |PARTITIONED|

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5170fb21/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-810.plan
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-810.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-810.plan
new file mode 100644
index 0000000..1c56f1c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-810.plan
@@ -0,0 +1,39 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- SORT_MERGE_EXCHANGE [$$l_returnflag(ASC), $$l_linestatus(ASC) ]  |PARTITIONED|
+          -- PRE_CLUSTERED_GROUP_BY[$$42, $$43]  |PARTITIONED|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              -- STABLE_SORT [$$42(ASC), $$43(ASC)]  |PARTITIONED|
+                -- HASH_PARTITION_EXCHANGE [$$42, $$43]  |PARTITIONED|
+                  -- PRE_CLUSTERED_GROUP_BY[$$32, $$33]  |PARTITIONED|
+                          {
+                            -- AGGREGATE  |LOCAL|
+                              -- STREAM_SELECT  |LOCAL|
+                                -- NESTED_TUPLE_SOURCE  |LOCAL|
+                          }
+                          {
+                            -- AGGREGATE  |LOCAL|
+                              -- STREAM_SELECT  |LOCAL|
+                                -- NESTED_TUPLE_SOURCE  |LOCAL|
+                          }
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- STABLE_SORT [$$32(ASC), $$33(ASC)]  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- ASSIGN  |PARTITIONED|
+                            -- STREAM_PROJECT  |PARTITIONED|
+                              -- ASSIGN  |PARTITIONED|
+                                -- STREAM_PROJECT  |PARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    -- DATASOURCE_SCAN  |PARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- EMPTY_TUPLE_SOURCE  |PARTITIONED|

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5170fb21/asterixdb/asterix-app/src/test/resources/optimizerts/results/udfs/query-ASTERIXDB-1308-1.plan
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/udfs/query-ASTERIXDB-1308-1.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/udfs/query-ASTERIXDB-1308-1.plan
index 1e60aed..f86825e 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results/udfs/query-ASTERIXDB-1308-1.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/udfs/query-ASTERIXDB-1308-1.plan
@@ -27,15 +27,10 @@
                                               -- AGGREGATE  |LOCAL|
                                                 -- AGGREGATE  |LOCAL|
                                                   -- STREAM_SELECT  |UNPARTITIONED|
-                                                    -- UNNEST  |UNPARTITIONED|
-                                                      -- SUBPLAN  |UNPARTITIONED|
-                                                              {
-                                                                -- AGGREGATE  |UNPARTITIONED|
-                                                                  -- IN_MEMORY_STABLE_SORT [$$j(ASC)]  |UNPARTITIONED|
-                                                                    -- UNNEST  |UNPARTITIONED|
-                                                                      -- NESTED_TUPLE_SOURCE  |UNPARTITIONED|
-                                                              }
-                                                        -- NESTED_TUPLE_SOURCE  |UNPARTITIONED|
+                                                    -- RUNNING_AGGREGATE  |UNPARTITIONED|
+                                                      -- IN_MEMORY_STABLE_SORT [$$j(ASC)]  |UNPARTITIONED|
+                                                        -- UNNEST  |UNPARTITIONED|
+                                                          -- NESTED_TUPLE_SOURCE  |UNPARTITIONED|
                                             }
                                       -- ASSIGN  |UNPARTITIONED|
                                         -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5170fb21/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/PreclusteredGroupByPOperator.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/PreclusteredGroupByPOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/PreclusteredGroupByPOperator.java
index a636d10..5a465f7 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/PreclusteredGroupByPOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/PreclusteredGroupByPOperator.java
@@ -23,6 +23,7 @@ import java.util.List;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.base.IHyracksJobBuilder;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
 import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
 import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
 import org.apache.hyracks.algebricks.core.algebra.base.PhysicalOperatorTag;
@@ -70,7 +71,8 @@ public class PreclusteredGroupByPOperator extends AbstractPreclusteredGroupByPOp
         AlgebricksPipeline[] subplans = compileSubplans(inputSchemas[0], gby, opSchema, context);
         IAggregatorDescriptorFactory aggregatorFactory;
 
-        if (gby.getNestedPlans().get(0).getRoots().get(0).getValue()
+        List<ILogicalPlan> nestedPlans = gby.getNestedPlans();
+        if (!nestedPlans.isEmpty() && nestedPlans.get(0).getRoots().get(0).getValue()
                 .getOperatorTag() == LogicalOperatorTag.RUNNINGAGGREGATE) {
             aggregatorFactory = new NestedPlansRunningAggregatorFactory(subplans, keys, fdColumns);
         } else {

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5170fb21/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/typing/AbstractTypeEnvironment.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/typing/AbstractTypeEnvironment.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/typing/AbstractTypeEnvironment.java
index c4ff55b..f530820 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/typing/AbstractTypeEnvironment.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/typing/AbstractTypeEnvironment.java
@@ -52,11 +52,10 @@ public abstract class AbstractTypeEnvironment implements IVariableTypeEnvironmen
 
     @Override
     public boolean substituteProducedVariable(LogicalVariable v1, LogicalVariable v2) throws AlgebricksException {
-        Object t = varTypeMap.get(v1);
+        Object t = varTypeMap.remove(v1);
         if (t == null) {
             return false;
         }
-        varTypeMap.put(v1, null);
         varTypeMap.put(v2, t);
         return true;
     }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5170fb21/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/AbstractIntroduceGroupByCombinerRule.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/AbstractIntroduceGroupByCombinerRule.java b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/AbstractIntroduceGroupByCombinerRule.java
index 4ef1cd5..e73f2a5 100644
--- a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/AbstractIntroduceGroupByCombinerRule.java
+++ b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/AbstractIntroduceGroupByCombinerRule.java
@@ -163,6 +163,11 @@ public abstract class AbstractIntroduceGroupByCombinerRule extends AbstractIntro
             }
         }
 
+        // Nothing is pushed.
+        if (bi.modifyGbyMap.isEmpty()) {
+            return null;
+        }
+
         ArrayList<LogicalVariable> newOpGbyList = new ArrayList<LogicalVariable>();
         ArrayList<LogicalVariable> replGbyList = new ArrayList<LogicalVariable>();
         // Find maximal sequence of variable.

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5170fb21/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/PushNestedOrderByUnderPreSortedGroupByRule.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/PushNestedOrderByUnderPreSortedGroupByRule.java b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/PushNestedOrderByUnderPreSortedGroupByRule.java
index bdab689..fc7b98e 100644
--- a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/PushNestedOrderByUnderPreSortedGroupByRule.java
+++ b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/PushNestedOrderByUnderPreSortedGroupByRule.java
@@ -64,6 +64,9 @@ public class PushNestedOrderByUnderPreSortedGroupByRule implements IAlgebraicRew
             return false;
         }
         GroupByOperator gby = (GroupByOperator) op;
+        if (gby.getNestedPlans().isEmpty()) {
+            return false;
+        }
         ILogicalPlan plan = gby.getNestedPlans().get(0);
         AbstractLogicalOperator op1 = (AbstractLogicalOperator) plan.getRoots().get(0).getValue();
         if (op1.getOperatorTag() != LogicalOperatorTag.AGGREGATE) {

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/5170fb21/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/subplan/EliminateSubplanWithInputCardinalityOneRule.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/subplan/EliminateSubplanWithInputCardinalityOneRule.java b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/subplan/EliminateSubplanWithInputCardinalityOneRule.java
index 2cd857a..607ea1f 100644
--- a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/subplan/EliminateSubplanWithInputCardinalityOneRule.java
+++ b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/subplan/EliminateSubplanWithInputCardinalityOneRule.java
@@ -65,27 +65,39 @@ public class EliminateSubplanWithInputCardinalityOneRule implements IAlgebraicRe
             rootRef = opRef;
             invoked = true;
         }
+        return rewriteForOperator(rootRef, opRef, context);
+    }
+
+    private boolean rewriteForOperator(Mutable<ILogicalOperator> rootRef, Mutable<ILogicalOperator> opRef,
+            IOptimizationContext context) throws AlgebricksException {
         AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();
         if (op.getInputs().size() <= 0) {
             return false;
         }
         boolean changed = false;
-        for (Mutable<ILogicalOperator> subplanRef : op.getInputs()) {
-            AbstractLogicalOperator op1 = (AbstractLogicalOperator) subplanRef.getValue();
+
+        for (Mutable<ILogicalOperator> currentOpRef : op.getInputs()) {
+            AbstractLogicalOperator op1 = (AbstractLogicalOperator) currentOpRef.getValue();
             if (op1.getOperatorTag() != LogicalOperatorTag.SUBPLAN) {
+                changed |= rewriteForOperator(rootRef, currentOpRef, context);
                 continue;
             }
 
             SubplanOperator subplan = (SubplanOperator) op1;
-            Set<LogicalVariable> usedVarsUp = new ListSet<LogicalVariable>();
+            Set<LogicalVariable> usedVarsUp = new ListSet<>();
             OperatorPropertiesUtil.getFreeVariablesInPath(rootRef.getValue(), subplan, usedVarsUp);
             // TODO(buyingyi): figure out the rewriting for subplan operators with multiple subplans.
             if (subplan.getNestedPlans().size() != 1) {
                 continue;
             }
 
+            // Recursively rewrites the pipelines inside a nested subplan.
+            for (Mutable<ILogicalOperator> nestedRootRef : subplan.getNestedPlans().get(0).getRoots()) {
+                changed |= this.rewriteForOperator(nestedRootRef, nestedRootRef, context);
+            }
+
             ILogicalOperator subplanInputOperator = subplan.getInputs().get(0).getValue();
-            Set<LogicalVariable> subplanInputVars = new ListSet<LogicalVariable>();
+            Set<LogicalVariable> subplanInputVars = new ListSet<>();
             VariableUtilities.getLiveVariables(subplanInputOperator, subplanInputVars);
             int subplanInputVarSize = subplanInputVars.size();
             subplanInputVars.removeAll(usedVarsUp);
@@ -93,31 +105,31 @@ public class EliminateSubplanWithInputCardinalityOneRule implements IAlgebraicRe
             if (subplanInputVars.size() < subplanInputVarSize) {
                 continue;
             }
-            Set<LogicalVariable> freeVars = new ListSet<LogicalVariable>();
+            Set<LogicalVariable> freeVars = new ListSet<>();
             OperatorPropertiesUtil.getFreeVariablesInSubplans(subplan, freeVars);
             boolean cardinalityOne = isCardinalityOne(subplan.getInputs().get(0), freeVars);
-            if (cardinalityOne) {
-                /** If the cardinality of freeVars in the subplan is one, the subplan can be removed. */
-                ILogicalPlan plan = subplan.getNestedPlans().get(0);
-
-                List<Mutable<ILogicalOperator>> rootRefs = plan.getRoots();
-                // TODO(buyingyi): investigate the case of multi-root plans.
-                if (rootRefs.size() != 1) {
-                    continue;
-                }
+            if (!cardinalityOne) {
+                continue;
+            }
+            /** If the cardinality of freeVars in the subplan is one, the subplan can be removed. */
+            ILogicalPlan plan = subplan.getNestedPlans().get(0);
 
-                // Replaces all Nts' in the nested plan with the Subplan input operator or its deep copy.
-                ILogicalOperator topOperator = rootRefs.get(0).getValue();
-                ReplaceNtsWithSubplanInputOperatorVisitor visitor = new ReplaceNtsWithSubplanInputOperatorVisitor(
-                        context, subplan);
-                ILogicalOperator newTopOperator = topOperator.accept(visitor, null);
-                subplanRef.setValue(newTopOperator);
-                OperatorManipulationUtil.computeTypeEnvironmentBottomUp(newTopOperator, context);
-                changed = true;
-            } else {
+            List<Mutable<ILogicalOperator>> rootRefs = plan.getRoots();
+            // TODO(buyingyi): investigate the case of multi-root plans.
+            if (rootRefs.size() != 1) {
                 continue;
             }
+
+            // Replaces all Nts' in the nested plan with the Subplan input operator or its deep copy.
+            ILogicalOperator topOperator = rootRefs.get(0).getValue();
+            ReplaceNtsWithSubplanInputOperatorVisitor visitor = new ReplaceNtsWithSubplanInputOperatorVisitor(context,
+                    subplan);
+            ILogicalOperator newTopOperator = topOperator.accept(visitor, null);
+            currentOpRef.setValue(newTopOperator);
+            OperatorManipulationUtil.computeTypeEnvironmentBottomUp(newTopOperator, context);
+            changed = true;
         }
+
         return changed;
     }
 
@@ -133,8 +145,8 @@ public class EliminateSubplanWithInputCardinalityOneRule implements IAlgebraicRe
      */
     private boolean isCardinalityOne(Mutable<ILogicalOperator> opRef, Set<LogicalVariable> freeVars)
             throws AlgebricksException {
-        Set<LogicalVariable> varsWithCardinalityOne = new ListSet<LogicalVariable>();
-        Set<LogicalVariable> varsLiveAtUnnestAndJoin = new ListSet<LogicalVariable>();
+        Set<LogicalVariable> varsWithCardinalityOne = new ListSet<>();
+        Set<LogicalVariable> varsLiveAtUnnestAndJoin = new ListSet<>();
         isCardinalityOne(opRef, freeVars, varsWithCardinalityOne, varsLiveAtUnnestAndJoin);
         varsWithCardinalityOne.removeAll(varsLiveAtUnnestAndJoin);
         return varsWithCardinalityOne.equals(freeVars);
@@ -158,7 +170,7 @@ public class EliminateSubplanWithInputCardinalityOneRule implements IAlgebraicRe
             Set<LogicalVariable> varsWithCardinalityOne, Set<LogicalVariable> varsLiveAtUnnestAndJoin)
                     throws AlgebricksException {
         AbstractLogicalOperator operator = (AbstractLogicalOperator) opRef.getValue();
-        List<LogicalVariable> liveVars = new ArrayList<LogicalVariable>();
+        List<LogicalVariable> liveVars = new ArrayList<>();
         VariableUtilities.getLiveVariables(operator, liveVars);
 
         if (OperatorPropertiesUtil.isCardinalityZeroOrOne(operator)) {
@@ -176,13 +188,6 @@ public class EliminateSubplanWithInputCardinalityOneRule implements IAlgebraicRe
                 VariableUtilities.getLiveVariables(operator, varsLiveAtUnnestAndJoin);
             }
         }
-
-        if (varsWithCardinalityOne.size() == freeVars.size()) {
-            return;
-        }
-        for (Mutable<ILogicalOperator> childRef : operator.getInputs()) {
-            isCardinalityOne(childRef, freeVars, varsWithCardinalityOne, varsLiveAtUnnestAndJoin);
-        }
     }
 
 }