You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by ti...@apache.org on 2016/09/07 04:39:25 UTC

[4/4] asterixdb git commit: ASTERIXDB-1581: fix subquery decorrelation.

ASTERIXDB-1581: fix subquery decorrelation.

- fix concurrent modification exception;
- fix variable substitution ordering;
- fix required partitioning property for DataSourceScan;
- fix partitioning property enforcer for nested operators;
- fix recursive subplan decorrelation;
- fix CardinalityInferenceVisitor;
- add a rule to switch inner join branches;
- fix SimpleUnnestToProductRule;
- add test cases which are variants of ASTERIXDB-1581.

Change-Id: Ia2fa4b5b836eafee1975bd1164ae7c22199a4af0
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1125
Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>
Reviewed-by: Till Westmann <ti...@apache.org>


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

Branch: refs/heads/master
Commit: c89d668f68e5430a6ba4455daf8f9cd6f7040dd8
Parents: ae2fbc6
Author: Yingyi Bu <yi...@couchbase.com>
Authored: Tue Sep 6 18:29:23 2016 -0700
Committer: Till Westmann <ti...@apache.org>
Committed: Tue Sep 6 21:38:37 2016 -0700

----------------------------------------------------------------------
 .../asterix/optimizer/base/RuleCollections.java |   2 +
 .../rules/am/InvertedIndexAccessMethod.java     |   7 +-
 .../subplan/InlineAllNtsInSubplanVisitor.java   |  16 +-
 ...ineSubplanInputForNestedTupleSourceRule.java | 154 +++++-----
 .../rules/subplan/SubplanFlatteningUtil.java    |  78 +++--
 .../LangExpressionToPlanTranslator.java         |  29 +-
 .../queries/join-singletonbranch-2.sqlpp        |  43 +++
 .../queries/join-singletonbranch-3.sqlpp        |  42 +++
 .../queries/join-singletonbranch.sqlpp          |  41 +++
 .../tpcds/query-ASTERIXDB-1581-correlated.sqlpp |  96 +++++++
 .../queries/tpcds/query-ASTERIXDB-1581.sqlpp    |  96 +++++++
 .../optimizerts/results/filter-nested.plan      |   4 +-
 .../results/join-singletonbranch-2.plan         |  28 ++
 .../results/join-singletonbranch-3.plan         |  23 ++
 .../results/join-singletonbranch.plan           |  28 ++
 .../optimizerts/results/nest_aggregate.plan     |   6 +-
 .../tpcds/query-ASTERIXDB-1581-correlated.plan  | 281 +++++++++++++++++++
 .../results/tpcds/query-ASTERIXDB-1581.plan     | 180 ++++++++++++
 .../results/udfs/query-ASTERIXDB-1018.plan      |  79 +++---
 .../results/udfs/query-ASTERIXDB-1019.plan      |  79 +++---
 .../aggregate_join/aggregate_join.1.ddl.sqlpp   |  31 ++
 .../aggregate_join.2.update.sqlpp               |  23 ++
 .../aggregate_join/aggregate_join.3.query.sqlpp |  26 ++
 .../aggregate_join_external.1.ddl.sqlpp         |  55 ++++
 .../aggregate_join_external.3.query.sqlpp       |  27 ++
 .../query-ASTERIXDB-1581-2.1.ddl.sqlpp          |  82 ++++++
 .../query-ASTERIXDB-1581-2.2.update.sqlpp       |  26 ++
 .../query-ASTERIXDB-1581-2.3.query.sqlpp        |  33 +++
 .../query-ASTERIXDB-1581-3.1.ddl.sqlpp          |  82 ++++++
 .../query-ASTERIXDB-1581-3.2.update.sqlpp       |  26 ++
 .../query-ASTERIXDB-1581-3.3.query.sqlpp        |  32 +++
 .../query-ASTERIXDB-1581-4.1.ddl.sqlpp          |  82 ++++++
 .../query-ASTERIXDB-1581-4.2.update.sqlpp       |  26 ++
 .../query-ASTERIXDB-1581-4.3.query.sqlpp        |  32 +++
 .../query-ASTERIXDB-1581-5.1.ddl.sqlpp          |  82 ++++++
 .../query-ASTERIXDB-1581-5.2.update.sqlpp       |  26 ++
 .../query-ASTERIXDB-1581-5.3.query.sqlpp        |  32 +++
 .../query-ASTERIXDB-1581-6.1.ddl.sqlpp          |  82 ++++++
 .../query-ASTERIXDB-1581-6.2.update.sqlpp       |  26 ++
 .../query-ASTERIXDB-1581-6.3.query.sqlpp        |  35 +++
 ...uery-ASTERIXDB-1581-correlated-2.1.ddl.sqlpp |  82 ++++++
 ...y-ASTERIXDB-1581-correlated-2.2.update.sqlpp |  26 ++
 ...ry-ASTERIXDB-1581-correlated-2.3.query.sqlpp |  34 +++
 .../query-ASTERIXDB-1581-correlated.1.ddl.sqlpp |  82 ++++++
 ...ery-ASTERIXDB-1581-correlated.2.update.sqlpp |  26 ++
 ...uery-ASTERIXDB-1581-correlated.3.query.sqlpp |  34 +++
 .../query-ASTERIXDB-1581.1.ddl.sqlpp            |  82 ++++++
 .../query-ASTERIXDB-1581.2.update.sqlpp         |  26 ++
 .../query-ASTERIXDB-1581.3.query.sqlpp          |  29 ++
 .../aggregate_join/aggregate_join.1.adm         |   1 +
 .../aggregate_join_external.1.adm               |   5 +
 .../query-ASTERIXDB-1581-2.1.adm                |   1 +
 .../query-ASTERIXDB-1581-4.1.adm                |   1 +
 .../query-ASTERIXDB-1581-6.1.adm                |   1 +
 .../query-ASTERIXDB-1581-correlated.1.adm       |   1 +
 .../query-ASTERIXDB-1581.1.adm                  |   1 +
 .../resources/runtimets/testsuite_sqlpp.xml     |  50 ++++
 .../metadata/declared/AqlDataSource.java        |   1 +
 .../core/algebra/metadata/IDataSource.java      |   3 +
 .../visitors/CardinalityInferenceVisitor.java   | 162 +++++++++--
 .../IsomorphismVariableMappingVisitor.java      |  29 +-
 ...OperatorDeepCopyWithNewVariablesVisitor.java |  34 ++-
 .../logical/visitors/VariableUtilities.java     |  23 +-
 .../physical/DataSourceScanPOperator.java       |  25 ++
 .../physical/EmptyTupleSourcePOperator.java     |   4 +-
 .../algebra/util/OperatorManipulationUtil.java  |   3 +-
 .../algebra/util/OperatorPropertiesUtil.java    |   7 +-
 .../piglet/metadata/PigletFileDataSource.java   |   7 +
 .../rules/EnforceStructuralPropertiesRule.java  |  13 +-
 ...moveCartesianProductWithEmptyBranchRule.java |   6 +-
 .../rules/SimpleUnnestToProductRule.java        |  74 +++--
 .../rules/SwitchInnerJoinBranchRule.java        |  61 ++++
 ...inateSubplanWithInputCardinalityOneRule.java |   2 +-
 ...placeNtsWithSubplanInputOperatorVisitor.java |   3 +-
 74 files changed, 2823 insertions(+), 294 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
index cf36c05..582cac9 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
@@ -122,6 +122,7 @@ import org.apache.hyracks.algebricks.rewriter.rules.RemoveUnusedAssignAndAggrega
 import org.apache.hyracks.algebricks.rewriter.rules.SetAlgebricksPhysicalOperatorsRule;
 import org.apache.hyracks.algebricks.rewriter.rules.SetExecutionModeRule;
 import org.apache.hyracks.algebricks.rewriter.rules.SimpleUnnestToProductRule;
+import org.apache.hyracks.algebricks.rewriter.rules.SwitchInnerJoinBranchRule;
 import org.apache.hyracks.algebricks.rewriter.rules.subplan.EliminateSubplanRule;
 import org.apache.hyracks.algebricks.rewriter.rules.subplan.EliminateSubplanWithInputCardinalityOneRule;
 import org.apache.hyracks.algebricks.rewriter.rules.subplan.NestedSubplanToJoinRule;
@@ -282,6 +283,7 @@ public final class RuleCollections {
 
     public static final List<IAlgebraicRewriteRule> buildPlanCleanupRuleCollection() {
         List<IAlgebraicRewriteRule> planCleanupRules = new LinkedList<>();
+        planCleanupRules.add(new SwitchInnerJoinBranchRule());
         planCleanupRules.add(new PushAssignBelowUnionAllRule());
         planCleanupRules.add(new ExtractCommonExpressionsRule());
         planCleanupRules.add(new RemoveRedundantVariablesRule());

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java
index 1052a72..0bc5e78 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java
@@ -21,6 +21,7 @@ package org.apache.asterix.optimizer.rules.am;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -64,7 +65,6 @@ import org.apache.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFun
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractBinaryJoinOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
-import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator.ExecutionMode;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractUnnestMapOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator;
@@ -73,6 +73,7 @@ import org.apache.hyracks.algebricks.core.algebra.operators.logical.ReplicateOpe
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnionAllOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator.ExecutionMode;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.LogicalOperatorDeepCopyWithNewVariablesVisitor;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
 import org.apache.hyracks.algebricks.core.algebra.util.OperatorManipulationUtil;
@@ -589,9 +590,9 @@ public class InvertedIndexAccessMethod implements IAccessMethod {
         // will have all primary-key and secondary-search keys replaced, but retains all other original variables.
 
         // Variable replacement map for the first copy.
-        Map<LogicalVariable, LogicalVariable> newProbeSubTreeVarMap = new HashMap<LogicalVariable, LogicalVariable>();
+        LinkedHashMap<LogicalVariable, LogicalVariable> newProbeSubTreeVarMap = new LinkedHashMap<>();
         // Variable replacement map for the second copy.
-        Map<LogicalVariable, LogicalVariable> joinInputSubTreeVarMap = new HashMap<LogicalVariable, LogicalVariable>();
+        LinkedHashMap<LogicalVariable, LogicalVariable> joinInputSubTreeVarMap = new LinkedHashMap<>();
         // Init with all live vars.
         List<LogicalVariable> liveVars = new ArrayList<LogicalVariable>();
         VariableUtilities.getLiveVariables(probeSubTree.getRoot(), liveVars);

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java
index b061066..8e5fa79 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java
@@ -23,10 +23,11 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
+import java.util.Map.Entry;
 
 import org.apache.asterix.lang.common.util.FunctionUtil;
 import org.apache.asterix.om.base.AString;
@@ -66,7 +67,6 @@ import org.apache.hyracks.algebricks.core.algebra.operators.logical.LimitOperato
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.MaterializeOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.NestedTupleSourceOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator;
-import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator.IOrder;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.PartitioningSplitOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.ProjectOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.ReplicateOperator;
@@ -78,6 +78,7 @@ import org.apache.hyracks.algebricks.core.algebra.operators.logical.TokenizeOper
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnionAllOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestMapOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator.IOrder;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.LogicalOperatorDeepCopyWithNewVariablesVisitor;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
 import org.apache.hyracks.algebricks.core.algebra.plan.ALogicalPlanImpl;
@@ -135,7 +136,8 @@ class InlineAllNtsInSubplanVisitor implements IQueryOperatorVisitor<ILogicalOper
 
     // Maps live variables at <code>subplanInputOperator</code> to variables in
     // the flattened nested plan.
-    private final Map<LogicalVariable, LogicalVariable> subplanInputVarToCurrentVarMap = new HashMap<>();
+    private final LinkedHashMap<LogicalVariable, LogicalVariable> subplanInputVarToCurrentVarMap = new
+            LinkedHashMap<>();
 
     // Maps variables in the flattened nested plan to live variables at
     // <code>subplannputOperator</code>.
@@ -154,7 +156,7 @@ class InlineAllNtsInSubplanVisitor implements IQueryOperatorVisitor<ILogicalOper
     /**
      * @param context
      *            the optimization context
-     * @param subplanInputOperator
+     * @param subplanOperator
      *            the input operator to the target subplan operator, which is to
      *            be inlined.
      * @throws AlgebricksException
@@ -625,8 +627,6 @@ class InlineAllNtsInSubplanVisitor implements IQueryOperatorVisitor<ILogicalOper
      *
      * @param op
      *            the logical operator for aggregate or running aggregate.
-     * @param keyVarsToEnforce
-     *            the set of variables that needs to preserve.
      * @return the wrapped group-by operator if {@code keyVarsToEnforce} is not
      *         empty, and {@code op} otherwise.
      * @throws AlgebricksException
@@ -637,7 +637,9 @@ class InlineAllNtsInSubplanVisitor implements IQueryOperatorVisitor<ILogicalOper
             return op;
         }
         GroupByOperator gbyOp = new GroupByOperator();
-        for (LogicalVariable keyVar : correlatedKeyVars) {
+        // Creates a copy of correlatedKeyVars, to fix the ConcurrentModificationExcetpion in ASTERIXDB-1581.
+        List<LogicalVariable> copyOfCorrelatedKeyVars = new ArrayList<>(correlatedKeyVars);
+        for (LogicalVariable keyVar : copyOfCorrelatedKeyVars) {
             // This limits the visitor can only be applied to a nested logical
             // plan inside a Subplan operator,
             // where the keyVarsToEnforce forms a candidate key which can

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineSubplanInputForNestedTupleSourceRule.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineSubplanInputForNestedTupleSourceRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineSubplanInputForNestedTupleSourceRule.java
index 1881461..2e368c0 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineSubplanInputForNestedTupleSourceRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineSubplanInputForNestedTupleSourceRule.java
@@ -19,8 +19,8 @@
 package org.apache.asterix.optimizer.rules.subplan;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -48,9 +48,9 @@ import org.apache.hyracks.algebricks.core.algebra.operators.logical.GroupByOpera
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterJoinOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.NestedTupleSourceOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator;
-import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator.IOrder;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.SubplanOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator.IOrder;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
 import org.apache.hyracks.algebricks.core.algebra.plan.ALogicalPlanImpl;
 import org.apache.hyracks.algebricks.core.algebra.util.OperatorManipulationUtil;
@@ -267,22 +267,26 @@ public class InlineSubplanInputForNestedTupleSourceRule implements IAlgebraicRew
         if (context.checkIfInDontApplySet(this, opRef.getValue())) {
             return false;
         }
-        Pair<Boolean, Map<LogicalVariable, LogicalVariable>> result = rewriteSubplanOperator(opRef, context);
+        Pair<Boolean, LinkedHashMap<LogicalVariable, LogicalVariable>> result = rewriteSubplanOperator(opRef, context);
         hasRun = true;
         return result.first;
     }
 
-    private Pair<Boolean, Map<LogicalVariable, LogicalVariable>> rewriteSubplanOperator(Mutable<ILogicalOperator> opRef,
+    private Pair<Boolean, LinkedHashMap<LogicalVariable, LogicalVariable>> rewriteSubplanOperator(
+            Mutable<ILogicalOperator> opRef,
             IOptimizationContext context) throws AlgebricksException {
         AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();
+        // Recursively traverses input operators as if the current operator before rewriting the current operator.
+        Pair<Boolean, LinkedHashMap<LogicalVariable, LogicalVariable>> changedAndVarMap = traverseNonSubplanOperator(op,
+                context);
         if (op.getOperatorTag() != LogicalOperatorTag.SUBPLAN) {
-            // Traverses non-subplan operators.
-            return traverseNonSubplanOperator(op, context);
+            return changedAndVarMap;
         }
+
         /**
          * Apply the special join-based rewriting.
          */
-        Pair<Boolean, Map<LogicalVariable, LogicalVariable>> result = applySpecialFlattening(opRef, context);
+        Pair<Boolean, LinkedHashMap<LogicalVariable, LogicalVariable>> result = applySpecialFlattening(opRef, context);
         if (!result.first) {
             /**
              * If the special join-based rewriting does not apply, apply the general
@@ -290,7 +294,12 @@ public class InlineSubplanInputForNestedTupleSourceRule implements IAlgebraicRew
              */
             result = applyGeneralFlattening(opRef, context);
         }
-        return result;
+        LinkedHashMap<LogicalVariable, LogicalVariable> returnedMap = new LinkedHashMap<>();
+        // Adds variable mappings from input operators.
+        returnedMap.putAll(changedAndVarMap.second);
+        // Adds variable mappings resulting from the rewriting of the current operator.
+        returnedMap.putAll(result.second);
+        return new Pair<>(result.first || changedAndVarMap.first, returnedMap);
     }
 
     /***
@@ -302,15 +311,17 @@ public class InlineSubplanInputForNestedTupleSourceRule implements IAlgebraicRew
      * @return
      * @throws AlgebricksException
      */
-    private Pair<Boolean, Map<LogicalVariable, LogicalVariable>> traverseNonSubplanOperator(ILogicalOperator op,
+    private Pair<Boolean, LinkedHashMap<LogicalVariable, LogicalVariable>> traverseNonSubplanOperator(
+            ILogicalOperator op,
             IOptimizationContext context) throws AlgebricksException {
         Set<LogicalVariable> liveVars = new HashSet<>();
         VariableUtilities.getLiveVariables(op, liveVars);
-        Map<LogicalVariable, LogicalVariable> replacedVarMap = new HashMap<LogicalVariable, LogicalVariable>();
-        Map<LogicalVariable, LogicalVariable> replacedVarMapForAncestor = new HashMap<LogicalVariable, LogicalVariable>();
+        LinkedHashMap<LogicalVariable, LogicalVariable> replacedVarMap = new LinkedHashMap<>();
+        LinkedHashMap<LogicalVariable, LogicalVariable> replacedVarMapForAncestor = new LinkedHashMap<>();
         boolean changed = false;
         for (Mutable<ILogicalOperator> childrenRef : op.getInputs()) {
-            Pair<Boolean, Map<LogicalVariable, LogicalVariable>> resultFromChild = rewriteSubplanOperator(childrenRef,
+            Pair<Boolean, LinkedHashMap<LogicalVariable, LogicalVariable>> resultFromChild = rewriteSubplanOperator(
+                    childrenRef,
                     context);
             changed = changed || resultFromChild.first;
             for (Map.Entry<LogicalVariable, LogicalVariable> entry : resultFromChild.second.entrySet()) {
@@ -323,18 +334,18 @@ public class InlineSubplanInputForNestedTupleSourceRule implements IAlgebraicRew
         }
         VariableUtilities.substituteVariables(op, replacedVarMap, context);
         context.computeAndSetTypeEnvironmentForOperator(op);
-        return new Pair<Boolean, Map<LogicalVariable, LogicalVariable>>(changed, replacedVarMapForAncestor);
+        return new Pair<>(changed, replacedVarMapForAncestor);
     }
 
-    private Pair<Boolean, Map<LogicalVariable, LogicalVariable>> applyGeneralFlattening(Mutable<ILogicalOperator> opRef,
+    private Pair<Boolean, LinkedHashMap<LogicalVariable, LogicalVariable>> applyGeneralFlattening(
+            Mutable<ILogicalOperator> opRef,
             IOptimizationContext context) throws AlgebricksException {
         SubplanOperator subplanOp = (SubplanOperator) opRef.getValue();
         if (!SubplanFlatteningUtil.containsOperators(subplanOp,
                 ImmutableSet.of(LogicalOperatorTag.DATASOURCESCAN, LogicalOperatorTag.INNERJOIN,
                         // We don't have nested runtime for union-all and distinct hence we have to include them here.
                         LogicalOperatorTag.LEFTOUTERJOIN, LogicalOperatorTag.UNIONALL, LogicalOperatorTag.DISTINCT))) {
-            // Traverses the operator as if it is not a subplan.
-            return traverseNonSubplanOperator(subplanOp, context);
+            return new Pair<>(false, new LinkedHashMap<>());
         }
         Mutable<ILogicalOperator> inputOpRef = subplanOp.getInputs().get(0);
         ILogicalOperator inputOpBackup = inputOpRef.getValue();
@@ -344,7 +355,7 @@ public class InlineSubplanInputForNestedTupleSourceRule implements IAlgebraicRew
         ILogicalOperator inputOp = primaryOpAndVars.first;
         Set<LogicalVariable> primaryKeyVars = primaryOpAndVars.second;
         inputOpRef.setValue(inputOp);
-        Set<LogicalVariable> inputLiveVars = new HashSet<LogicalVariable>();
+        Set<LogicalVariable> inputLiveVars = new HashSet<>();
         VariableUtilities.getLiveVariables(inputOp, inputLiveVars);
 
         Pair<Map<LogicalVariable, LogicalVariable>, List<Pair<IOrder, Mutable<ILogicalExpression>>>> varMapAndOrderExprs = SubplanFlatteningUtil
@@ -352,32 +363,31 @@ public class InlineSubplanInputForNestedTupleSourceRule implements IAlgebraicRew
         Map<LogicalVariable, LogicalVariable> varMap = varMapAndOrderExprs.first;
         if (varMap == null) {
             inputOpRef.setValue(inputOpBackup);
-            // Traverses the operator as if it is not a subplan.
-            return traverseNonSubplanOperator(subplanOp, context);
+            return new Pair<>(false, new LinkedHashMap<>());
         }
 
-        Mutable<ILogicalOperator> rightInputOpRef = subplanOp.getNestedPlans().get(0).getRoots().get(0).getValue()
-                .getInputs().get(0);
+        Mutable<ILogicalOperator> lowestAggregateRefInSubplan = SubplanFlatteningUtil
+                .findLowestAggregate(subplanOp.getNestedPlans().get(0).getRoots().get(0));
+        Mutable<ILogicalOperator> rightInputOpRef = lowestAggregateRefInSubplan.getValue().getInputs().get(0);
         ILogicalOperator rightInputOp = rightInputOpRef.getValue();
 
         // Creates a variable to indicate whether a left input tuple is killed in the plan rooted at rightInputOp.
         LogicalVariable assignVar = context.newVar();
-        ILogicalOperator assignOp = new AssignOperator(assignVar,
-                new MutableObject<ILogicalExpression>(ConstantExpression.TRUE));
+        ILogicalOperator assignOp = new AssignOperator(assignVar, new MutableObject<>(ConstantExpression.TRUE));
         assignOp.getInputs().add(rightInputOpRef);
         context.computeAndSetTypeEnvironmentForOperator(assignOp);
-        rightInputOpRef = new MutableObject<ILogicalOperator>(assignOp);
+        rightInputOpRef = new MutableObject<>(assignOp);
 
         // Constructs the join predicate for the leftOuter join.
-        List<Mutable<ILogicalExpression>> joinPredicates = new ArrayList<Mutable<ILogicalExpression>>();
+        List<Mutable<ILogicalExpression>> joinPredicates = new ArrayList<>();
         for (LogicalVariable liveVar : primaryKeyVars) {
-            List<Mutable<ILogicalExpression>> arguments = new ArrayList<Mutable<ILogicalExpression>>();
-            arguments.add(new MutableObject<ILogicalExpression>(new VariableReferenceExpression(liveVar)));
+            List<Mutable<ILogicalExpression>> arguments = new ArrayList<>();
+            arguments.add(new MutableObject<>(new VariableReferenceExpression(liveVar)));
             LogicalVariable rightVar = varMap.get(liveVar);
-            arguments.add(new MutableObject<ILogicalExpression>(new VariableReferenceExpression(rightVar)));
+            arguments.add(new MutableObject<>(new VariableReferenceExpression(rightVar)));
             ILogicalExpression expr = new ScalarFunctionCallExpression(
                     FunctionUtil.getFunctionInfo(AlgebricksBuiltinFunctions.EQ), arguments);
-            joinPredicates.add(new MutableObject<ILogicalExpression>(expr));
+            joinPredicates.add(new MutableObject<>(expr));
         }
 
         ILogicalExpression joinExpr = joinPredicates.size() > 1
@@ -385,21 +395,20 @@ public class InlineSubplanInputForNestedTupleSourceRule implements IAlgebraicRew
                         joinPredicates)
                 : joinPredicates.size() > 0 ? joinPredicates.get(0).getValue() : ConstantExpression.TRUE;
         LeftOuterJoinOperator leftOuterJoinOp = new LeftOuterJoinOperator(
-                new MutableObject<ILogicalExpression>(joinExpr), inputOpRef, rightInputOpRef);
+                new MutableObject<>(joinExpr), inputOpRef, rightInputOpRef);
         OperatorManipulationUtil.computeTypeEnvironmentBottomUp(rightInputOp, context);
         context.computeAndSetTypeEnvironmentForOperator(leftOuterJoinOp);
 
         // Creates group-by operator.
         List<Pair<LogicalVariable, Mutable<ILogicalExpression>>> groupByList = new ArrayList<Pair<LogicalVariable, Mutable<ILogicalExpression>>>();
         List<Pair<LogicalVariable, Mutable<ILogicalExpression>>> groupByDecorList = new ArrayList<Pair<LogicalVariable, Mutable<ILogicalExpression>>>();
-        List<ILogicalPlan> nestedPlans = new ArrayList<ILogicalPlan>();
+        List<ILogicalPlan> nestedPlans = new ArrayList<>();
         GroupByOperator groupbyOp = new GroupByOperator(groupByList, groupByDecorList, nestedPlans);
 
-        Map<LogicalVariable, LogicalVariable> replacedVarMap = new HashMap<>();
+        LinkedHashMap<LogicalVariable, LogicalVariable> replacedVarMap = new LinkedHashMap<>();
         for (LogicalVariable liveVar : primaryKeyVars) {
             LogicalVariable newVar = context.newVar();
-            groupByList.add(new Pair<LogicalVariable, Mutable<ILogicalExpression>>(newVar,
-                    new MutableObject<ILogicalExpression>(new VariableReferenceExpression(liveVar))));
+            groupByList.add(new Pair<>(newVar, new MutableObject<>(new VariableReferenceExpression(liveVar))));
             // Adds variables for replacements in ancestors.
             replacedVarMap.put(liveVar, newVar);
         }
@@ -407,44 +416,41 @@ public class InlineSubplanInputForNestedTupleSourceRule implements IAlgebraicRew
             if (primaryKeyVars.contains(liveVar)) {
                 continue;
             }
-            groupByDecorList.add(new Pair<LogicalVariable, Mutable<ILogicalExpression>>(null,
-                    new MutableObject<ILogicalExpression>(new VariableReferenceExpression(liveVar))));
+            groupByDecorList.add(new Pair<>(null, new MutableObject<>(new VariableReferenceExpression(liveVar))));
         }
 
         // Sets up the nested plan for the groupby operator.
         Mutable<ILogicalOperator> aggOpRef = subplanOp.getNestedPlans().get(0).getRoots().get(0);
-        aggOpRef.getValue().getInputs().clear();
-
-        Mutable<ILogicalOperator> currentOpRef = aggOpRef;
+        lowestAggregateRefInSubplan.getValue().getInputs().clear(); // Clears the input of the lowest aggregate.
+        Mutable<ILogicalOperator> currentOpRef = lowestAggregateRefInSubplan;
         // Adds an optional order operator.
         List<Pair<IOrder, Mutable<ILogicalExpression>>> orderExprs = varMapAndOrderExprs.second;
         if (!orderExprs.isEmpty()) {
             OrderOperator orderOp = new OrderOperator(orderExprs);
-            currentOpRef = new MutableObject<ILogicalOperator>(orderOp);
-            aggOpRef.getValue().getInputs().add(currentOpRef);
+            currentOpRef = new MutableObject<>(orderOp);
+            lowestAggregateRefInSubplan.getValue().getInputs().add(currentOpRef);
         }
 
         // Adds a select operator into the nested plan for group-by to remove tuples with NULL on {@code assignVar}, i.e.,
         // subplan input tuples that are filtered out within a subplan.
-        Mutable<ILogicalExpression> filterVarExpr = new MutableObject<ILogicalExpression>(
+        Mutable<ILogicalExpression> filterVarExpr = new MutableObject<>(
                 new VariableReferenceExpression(assignVar));
-        List<Mutable<ILogicalExpression>> args = new ArrayList<Mutable<ILogicalExpression>>();
+        List<Mutable<ILogicalExpression>> args = new ArrayList<>();
         args.add(filterVarExpr);
-        List<Mutable<ILogicalExpression>> argsForNotFunction = new ArrayList<Mutable<ILogicalExpression>>();
-        argsForNotFunction.add(new MutableObject<ILogicalExpression>(new ScalarFunctionCallExpression(
+        List<Mutable<ILogicalExpression>> argsForNotFunction = new ArrayList<>();
+        argsForNotFunction.add(new MutableObject<>(new ScalarFunctionCallExpression(
                 FunctionUtil.getFunctionInfo(AsterixBuiltinFunctions.IS_MISSING), args)));
         SelectOperator selectOp = new SelectOperator(
-                new MutableObject<ILogicalExpression>(new ScalarFunctionCallExpression(
+                new MutableObject<>(new ScalarFunctionCallExpression(
                         FunctionUtil.getFunctionInfo(AsterixBuiltinFunctions.NOT), argsForNotFunction)),
                 false, null);
-        currentOpRef.getValue().getInputs().add(new MutableObject<ILogicalOperator>(selectOp));
+        currentOpRef.getValue().getInputs().add(new MutableObject<>(selectOp));
 
-        selectOp.getInputs().add(new MutableObject<ILogicalOperator>(
-                new NestedTupleSourceOperator(new MutableObject<ILogicalOperator>(groupbyOp))));
-        List<Mutable<ILogicalOperator>> nestedRoots = new ArrayList<Mutable<ILogicalOperator>>();
+        selectOp.getInputs().add(new MutableObject<>(new NestedTupleSourceOperator(new MutableObject<>(groupbyOp))));
+        List<Mutable<ILogicalOperator>> nestedRoots = new ArrayList<>();
         nestedRoots.add(aggOpRef);
         nestedPlans.add(new ALogicalPlanImpl(nestedRoots));
-        groupbyOp.getInputs().add(new MutableObject<ILogicalOperator>(leftOuterJoinOp));
+        groupbyOp.getInputs().add(new MutableObject<>(leftOuterJoinOp));
 
         // Replaces subplan with the group-by operator.
         opRef.setValue(groupbyOp);
@@ -452,23 +458,25 @@ public class InlineSubplanInputForNestedTupleSourceRule implements IAlgebraicRew
 
         // Recursively applys this rule to the nested plan of the subplan operator,
         // for the case where there are nested subplan operators within {@code subplanOp}.
-        Pair<Boolean, Map<LogicalVariable, LogicalVariable>> result = rewriteSubplanOperator(rightInputOpRef, context);
+        Pair<Boolean, LinkedHashMap<LogicalVariable, LogicalVariable>> result = rewriteSubplanOperator(rightInputOpRef,
+                context);
         VariableUtilities.substituteVariables(leftOuterJoinOp, result.second, context);
         VariableUtilities.substituteVariables(groupbyOp, result.second, context);
 
         // No var mapping from the right input operator should be populated up.
-        return new Pair<Boolean, Map<LogicalVariable, LogicalVariable>>(true, replacedVarMap);
+        return new Pair<>(true, replacedVarMap);
     }
 
-    private Pair<Boolean, Map<LogicalVariable, LogicalVariable>> applySpecialFlattening(Mutable<ILogicalOperator> opRef,
+    private Pair<Boolean, LinkedHashMap<LogicalVariable, LogicalVariable>> applySpecialFlattening(
+            Mutable<ILogicalOperator> opRef,
             IOptimizationContext context) throws AlgebricksException {
         SubplanOperator subplanOp = (SubplanOperator) opRef.getValue();
         Mutable<ILogicalOperator> inputOpRef = subplanOp.getInputs().get(0);
-        Map<LogicalVariable, LogicalVariable> replacedVarMap = new HashMap<>();
+        LinkedHashMap<LogicalVariable, LogicalVariable> replacedVarMap = new LinkedHashMap<>();
 
         // Recursively applies this rule to the nested plan of the subplan operator,
         // for the case where there are nested subplan operators within {@code subplanOp}.
-        Pair<Boolean, Map<LogicalVariable, LogicalVariable>> result = rewriteSubplanOperator(
+        Pair<Boolean, LinkedHashMap<LogicalVariable, LogicalVariable>> result = rewriteSubplanOperator(
                 subplanOp.getNestedPlans().get(0).getRoots().get(0), context);
 
         ILogicalOperator inputOpBackup = inputOpRef.getValue();
@@ -485,7 +493,7 @@ public class InlineSubplanInputForNestedTupleSourceRule implements IAlgebraicRew
                 .inlineLeftNtsInSubplanJoin(subplanOp, context);
         if (notNullVarsAndTopJoinRef.first == null) {
             inputOpRef.setValue(inputOpBackup);
-            return new Pair<Boolean, Map<LogicalVariable, LogicalVariable>>(false, replacedVarMap);
+            return new Pair<>(false, replacedVarMap);
         }
 
         Set<LogicalVariable> notNullVars = notNullVarsAndTopJoinRef.first;
@@ -496,12 +504,9 @@ public class InlineSubplanInputForNestedTupleSourceRule implements IAlgebraicRew
         List<Pair<LogicalVariable, Mutable<ILogicalExpression>>> groupByDecorList = new ArrayList<Pair<LogicalVariable, Mutable<ILogicalExpression>>>();
         GroupByOperator groupbyOp = new GroupByOperator(groupByList, groupByDecorList, subplanOp.getNestedPlans());
 
-        Map<LogicalVariable, LogicalVariable> gbyVarMap = new HashMap<LogicalVariable, LogicalVariable>();
         for (LogicalVariable coverVar : primaryKeyVars) {
             LogicalVariable newVar = context.newVar();
-            gbyVarMap.put(coverVar, newVar);
-            groupByList.add(new Pair<LogicalVariable, Mutable<ILogicalExpression>>(newVar,
-                    new MutableObject<ILogicalExpression>(new VariableReferenceExpression(coverVar))));
+            groupByList.add(new Pair<>(newVar, new MutableObject<>(new VariableReferenceExpression(coverVar))));
             // Adds variables for replacements in ancestors.
             replacedVarMap.put(coverVar, newVar);
         }
@@ -509,44 +514,43 @@ public class InlineSubplanInputForNestedTupleSourceRule implements IAlgebraicRew
             if (primaryKeyVars.contains(liveVar)) {
                 continue;
             }
-            groupByDecorList.add(new Pair<LogicalVariable, Mutable<ILogicalExpression>>(null,
-                    new MutableObject<ILogicalExpression>(new VariableReferenceExpression(liveVar))));
+            groupByDecorList.add(new Pair<>(null, new MutableObject<>(new VariableReferenceExpression(liveVar))));
         }
-        groupbyOp.getInputs().add(new MutableObject<ILogicalOperator>(topJoinRef.getValue()));
+        groupbyOp.getInputs().add(new MutableObject<>(topJoinRef.getValue()));
 
-        if (notNullVars.size() > 0) {
+        if (!notNullVars.isEmpty()) {
             // Adds a select operator into the nested plan for group-by to remove tuples with NULL on {@code assignVar}, i.e.,
             // subplan input tuples that are filtered out within a subplan.
             List<Mutable<ILogicalExpression>> nullCheckExprRefs = new ArrayList<>();
             for (LogicalVariable notNullVar : notNullVars) {
-                Mutable<ILogicalExpression> filterVarExpr = new MutableObject<ILogicalExpression>(
+                Mutable<ILogicalExpression> filterVarExpr = new MutableObject<>(
                         new VariableReferenceExpression(notNullVar));
-                List<Mutable<ILogicalExpression>> args = new ArrayList<Mutable<ILogicalExpression>>();
+                List<Mutable<ILogicalExpression>> args = new ArrayList<>();
                 args.add(filterVarExpr);
-                List<Mutable<ILogicalExpression>> argsForNotFunction = new ArrayList<Mutable<ILogicalExpression>>();
-                argsForNotFunction.add(new MutableObject<ILogicalExpression>(new ScalarFunctionCallExpression(
+                List<Mutable<ILogicalExpression>> argsForNotFunction = new ArrayList<>();
+                argsForNotFunction.add(new MutableObject<>(new ScalarFunctionCallExpression(
                         FunctionUtil.getFunctionInfo(AsterixBuiltinFunctions.IS_MISSING), args)));
-                nullCheckExprRefs.add(new MutableObject<ILogicalExpression>(new ScalarFunctionCallExpression(
+                nullCheckExprRefs.add(new MutableObject<>(new ScalarFunctionCallExpression(
                         FunctionUtil.getFunctionInfo(AsterixBuiltinFunctions.NOT), argsForNotFunction)));
             }
             Mutable<ILogicalExpression> selectExprRef = nullCheckExprRefs.size() > 1
-                    ? new MutableObject<ILogicalExpression>(new ScalarFunctionCallExpression(
+                    ? new MutableObject<>(new ScalarFunctionCallExpression(
                             FunctionUtil.getFunctionInfo(AsterixBuiltinFunctions.AND), nullCheckExprRefs))
                     : nullCheckExprRefs.get(0);
             SelectOperator selectOp = new SelectOperator(selectExprRef, false, null);
             topJoinRef.setValue(selectOp);
-            selectOp.getInputs().add(new MutableObject<ILogicalOperator>(
-                    new NestedTupleSourceOperator(new MutableObject<ILogicalOperator>(groupbyOp))));
+            selectOp.getInputs()
+                    .add(new MutableObject<>(new NestedTupleSourceOperator(new MutableObject<>(groupbyOp))));
         } else {
             // The original join operator in the Subplan is a left-outer join.
             // Therefore, no null-check variable is injected and no SelectOperator needs to be added.
-            topJoinRef.setValue(new NestedTupleSourceOperator(new MutableObject<ILogicalOperator>(groupbyOp)));
+            topJoinRef.setValue(new NestedTupleSourceOperator(new MutableObject<>(groupbyOp)));
         }
         opRef.setValue(groupbyOp);
         OperatorManipulationUtil.computeTypeEnvironmentBottomUp(groupbyOp, context);
 
         VariableUtilities.substituteVariables(groupbyOp, result.second, context);
         replacedVarMap.putAll(result.second);
-        return new Pair<Boolean, Map<LogicalVariable, LogicalVariable>>(true, replacedVarMap);
+        return new Pair<>(true, replacedVarMap);
     }
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/SubplanFlatteningUtil.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/SubplanFlatteningUtil.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/SubplanFlatteningUtil.java
index 6d91923..377e96d 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/SubplanFlatteningUtil.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/SubplanFlatteningUtil.java
@@ -31,8 +31,8 @@ import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
 import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
 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.operators.logical.OrderOperator.IOrder;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.SubplanOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator.IOrder;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
 import org.apache.hyracks.algebricks.core.algebra.util.OperatorManipulationUtil;
 
@@ -62,7 +62,7 @@ class SubplanFlatteningUtil {
         InlineAllNtsInSubplanVisitor visitor = new InlineAllNtsInSubplanVisitor(context, subplanOp);
 
         // Rewrites the query plan.
-        ILogicalOperator topOp = subplanOp.getNestedPlans().get(0).getRoots().get(0).getValue();
+        ILogicalOperator topOp = findLowestAggregate(subplanOp.getNestedPlans().get(0).getRoots().get(0)).getValue();
         ILogicalOperator opToVisit = topOp.getInputs().get(0).getValue();
         ILogicalOperator result = opToVisit.accept(visitor, null);
         topOp.getInputs().get(0).setValue(result);
@@ -143,6 +143,54 @@ class SubplanFlatteningUtil {
     }
 
     /**
+     * Whether the query plan rooted {@code currentOp} contains a data source scan operator,
+     * with considering nested subplans.
+     *
+     * @param currentOp
+     *            the current operator
+     * @return true if {@code currentOp} contains a data source scan operator; false otherwise.
+     */
+    public static boolean containsOperatorsInternal(ILogicalOperator currentOp,
+            Set<LogicalOperatorTag> interestedOperatorTags) {
+        if (interestedOperatorTags.contains(currentOp.getOperatorTag())) {
+            return true;
+        }
+        if (currentOp.getOperatorTag() == LogicalOperatorTag.SUBPLAN
+                && containsOperators((SubplanOperator) currentOp, interestedOperatorTags)) {
+                return true;
+        }
+        for (Mutable<ILogicalOperator> childRef : currentOp.getInputs()) {
+            if (containsOperatorsInternal(childRef.getValue(), interestedOperatorTags)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Finds the lowest aggregate operator that should be put into a nested aggregation pipeline within a group-by
+     * operator.
+     * <p/>
+     * Note that neither binary input operators nor data scan can be put into a group by operator.
+     *
+     * @param currentOpRef,
+     *            the current root operator reference to look at.
+     * @return the operator reference of the lowest qualified aggregate operator.
+     */
+    public static Mutable<ILogicalOperator> findLowestAggregate(Mutable<ILogicalOperator> currentOpRef) {
+        ILogicalOperator currentOp = currentOpRef.getValue();
+        // Neither binary input operators nor data scan can be put into a group by operator.
+        if (currentOp.getInputs().size() != 1 || currentOp.getOperatorTag() == LogicalOperatorTag.DATASOURCESCAN) {
+            return null;
+        }
+        Mutable<ILogicalOperator> childReturn = findLowestAggregate(currentOp.getInputs().get(0));
+        if (childReturn == null) {
+            return currentOp.getOperatorTag() == LogicalOperatorTag.AGGREGATE ? currentOpRef : null;
+        }
+        return childReturn;
+    }
+
+    /**
      * Determine whether a subplan could be rewritten as a join-related special case.
      * The conditions include:
      * a. there is a join (let's call it J1.) in the nested plan,
@@ -174,30 +222,4 @@ class SubplanFlatteningUtil {
         return new Pair<Boolean, ILogicalOperator>(true, visitor.getQualifiedNts());
     }
 
-    /**
-     * Whether the query plan rooted {@code currentOp} contains a data source scan operator,
-     * with considering nested subplans.
-     *
-     * @param currentOp
-     *            the current operator
-     * @return true if {@code currentOp} contains a data source scan operator; false otherwise.
-     */
-    private static boolean containsOperatorsInternal(ILogicalOperator currentOp,
-            Set<LogicalOperatorTag> interestedOperatorTags) {
-        if (interestedOperatorTags.contains(currentOp.getOperatorTag())) {
-            return true;
-        }
-        if (currentOp.getOperatorTag() == LogicalOperatorTag.SUBPLAN) {
-            if (containsOperators((SubplanOperator) currentOp, interestedOperatorTags)) {
-                return true;
-            }
-        }
-        for (Mutable<ILogicalOperator> childRef : currentOp.getInputs()) {
-            if (containsOperatorsInternal(childRef.getValue(), interestedOperatorTags)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
index 56a5a57..b73ef82 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
@@ -22,13 +22,13 @@ import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
+import java.util.Map.Entry;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.asterix.algebra.base.ILangExpressionToPlanTranslator;
@@ -39,15 +39,15 @@ import org.apache.asterix.common.functions.FunctionConstants;
 import org.apache.asterix.common.functions.FunctionSignature;
 import org.apache.asterix.lang.aql.util.RangeMapBuilder;
 import org.apache.asterix.lang.common.base.Expression;
-import org.apache.asterix.lang.common.base.Expression.Kind;
 import org.apache.asterix.lang.common.base.ILangExpression;
 import org.apache.asterix.lang.common.base.Statement;
+import org.apache.asterix.lang.common.base.Expression.Kind;
 import org.apache.asterix.lang.common.clause.GroupbyClause;
 import org.apache.asterix.lang.common.clause.LetClause;
 import org.apache.asterix.lang.common.clause.LimitClause;
 import org.apache.asterix.lang.common.clause.OrderbyClause;
-import org.apache.asterix.lang.common.clause.OrderbyClause.OrderModifier;
 import org.apache.asterix.lang.common.clause.WhereClause;
+import org.apache.asterix.lang.common.clause.OrderbyClause.OrderModifier;
 import org.apache.asterix.lang.common.expression.CallExpr;
 import org.apache.asterix.lang.common.expression.FieldAccessor;
 import org.apache.asterix.lang.common.expression.FieldBinding;
@@ -55,14 +55,14 @@ import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
 import org.apache.asterix.lang.common.expression.IfExpr;
 import org.apache.asterix.lang.common.expression.IndexAccessor;
 import org.apache.asterix.lang.common.expression.ListConstructor;
-import org.apache.asterix.lang.common.expression.ListConstructor.Type;
 import org.apache.asterix.lang.common.expression.LiteralExpr;
 import org.apache.asterix.lang.common.expression.OperatorExpr;
 import org.apache.asterix.lang.common.expression.QuantifiedExpression;
-import org.apache.asterix.lang.common.expression.QuantifiedExpression.Quantifier;
 import org.apache.asterix.lang.common.expression.RecordConstructor;
 import org.apache.asterix.lang.common.expression.UnaryExpr;
 import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.expression.ListConstructor.Type;
+import org.apache.asterix.lang.common.expression.QuantifiedExpression.Quantifier;
 import org.apache.asterix.lang.common.literal.StringLiteral;
 import org.apache.asterix.lang.common.statement.FunctionDecl;
 import org.apache.asterix.lang.common.statement.Query;
@@ -73,13 +73,13 @@ import org.apache.asterix.lang.common.util.FunctionUtil;
 import org.apache.asterix.lang.common.visitor.base.AbstractQueryExpressionVisitor;
 import org.apache.asterix.metadata.MetadataException;
 import org.apache.asterix.metadata.MetadataManager;
-import org.apache.asterix.metadata.declared.AqlDataSource.AqlDataSourceType;
 import org.apache.asterix.metadata.declared.AqlMetadataProvider;
 import org.apache.asterix.metadata.declared.AqlSourceId;
 import org.apache.asterix.metadata.declared.DatasetDataSource;
 import org.apache.asterix.metadata.declared.LoadableDataSource;
 import org.apache.asterix.metadata.declared.ResultSetDataSink;
 import org.apache.asterix.metadata.declared.ResultSetSinkId;
+import org.apache.asterix.metadata.declared.AqlDataSource.AqlDataSourceType;
 import org.apache.asterix.metadata.entities.Dataset;
 import org.apache.asterix.metadata.entities.Function;
 import org.apache.asterix.metadata.entities.InternalDatasetDetails;
@@ -115,15 +115,15 @@ 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.OperatorAnnotations;
 import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
-import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression.FunctionKind;
 import org.apache.hyracks.algebricks.core.algebra.expressions.AggregateFunctionCallExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.BroadcastExpressionAnnotation;
-import org.apache.hyracks.algebricks.core.algebra.expressions.BroadcastExpressionAnnotation.BroadcastSide;
 import org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionAnnotation;
 import org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.UnnestingFunctionCallExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression.FunctionKind;
+import org.apache.hyracks.algebricks.core.algebra.expressions.BroadcastExpressionAnnotation.BroadcastSide;
 import org.apache.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
@@ -139,13 +139,13 @@ import org.apache.hyracks.algebricks.core.algebra.operators.logical.InsertDelete
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.LimitOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.NestedTupleSourceOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator;
-import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator.IOrder.OrderKind;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.ProjectOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.SinkOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.SubplanOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnionAllOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator.IOrder.OrderKind;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.LogicalOperatorDeepCopyWithNewVariablesVisitor;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
 import org.apache.hyracks.algebricks.core.algebra.plan.ALogicalPlanImpl;
@@ -1526,7 +1526,7 @@ class LangExpressionToPlanTranslator
      *         <code>currentOpRef</code> to replace variables properly.
      * @throws AsterixException
      */
-    private Map<LogicalVariable, LogicalVariable> eliminateSharedOperatorReference(
+    private LinkedHashMap<LogicalVariable, LogicalVariable> eliminateSharedOperatorReference(
             Mutable<ILogicalOperator> currentOpRef, Set<Mutable<ILogicalOperator>> opRefSet) throws AsterixException {
         try {
             opRefSet.add(currentOpRef);
@@ -1547,7 +1547,7 @@ class LangExpressionToPlanTranslator
             }
 
             int childIndex = 0;
-            Map<LogicalVariable, LogicalVariable> varMap = new HashMap<>();
+            LinkedHashMap<LogicalVariable, LogicalVariable> varMap = new LinkedHashMap<>();
             for (Mutable<ILogicalOperator> childRef : currentOperator.getInputs()) {
                 if (opRefSet.contains(childRef)) {
                     // There is a shared operator reference in the query plan.
@@ -1555,7 +1555,8 @@ class LangExpressionToPlanTranslator
                     LogicalOperatorDeepCopyWithNewVariablesVisitor visitor =
                             new LogicalOperatorDeepCopyWithNewVariablesVisitor(context, null);
                     ILogicalOperator newChild = childRef.getValue().accept(visitor, null);
-                    Map<LogicalVariable, LogicalVariable> cloneVarMap = visitor.getInputToOutputVariableMapping();
+                    LinkedHashMap<LogicalVariable, LogicalVariable> cloneVarMap = visitor
+                            .getInputToOutputVariableMapping();
 
                     // Substitute variables according to the deep copy which generates new variables.
                     VariableUtilities.substituteVariables(currentOperator, cloneVarMap, null);
@@ -1568,7 +1569,7 @@ class LangExpressionToPlanTranslator
 
                 // Recursively eliminate shared operator reference for the operator subtree,
                 // even if it is a deep copy of some other one.
-                Map<LogicalVariable, LogicalVariable> childVarMap = eliminateSharedOperatorReference(childRef,
+                LinkedHashMap<LogicalVariable, LogicalVariable> childVarMap = eliminateSharedOperatorReference(childRef,
                         opRefSet);
                 // Substitute variables according to the new subtree.
                 VariableUtilities.substituteVariables(currentOperator, childVarMap, null);

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-app/src/test/resources/optimizerts/queries/join-singletonbranch-2.sqlpp
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/join-singletonbranch-2.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/join-singletonbranch-2.sqlpp
new file mode 100644
index 0000000..6a85b6a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/join-singletonbranch-2.sqlpp
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+drop dataverse test if exists;
+
+create dataverse test;
+
+use test;
+
+create type FooType as {
+  fid: int32
+}
+
+create type BarType as {
+  bid: int32
+}
+
+create dataset Foo(FooType) primary key fid;
+
+create dataset Bar(BarType) primary key bid;
+
+SELECT *
+FROM (SELECT AVG(f.fee) foo_avg FROM Foo f) f
+JOIN Bar b
+ON b.fee < f.avg;
+
+drop dataverse test;

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-app/src/test/resources/optimizerts/queries/join-singletonbranch-3.sqlpp
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/join-singletonbranch-3.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/join-singletonbranch-3.sqlpp
new file mode 100644
index 0000000..e555352
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/join-singletonbranch-3.sqlpp
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+drop dataverse test if exists;
+
+create dataverse test;
+
+use test;
+
+create type FooType as {
+  fid: int32
+}
+
+create type BarType as {
+  bid: int32
+}
+
+create dataset Foo(FooType) primary key fid;
+
+create dataset Bar(BarType) primary key bid;
+
+SELECT *
+FROM (SELECT AVG(f.fee) foo_avg FROM Foo f) f, Bar b
+WHERE b.fee < f.avg;
+
+drop dataverse test;

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-app/src/test/resources/optimizerts/queries/join-singletonbranch.sqlpp
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/join-singletonbranch.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/join-singletonbranch.sqlpp
new file mode 100644
index 0000000..2b92d97
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/join-singletonbranch.sqlpp
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+drop dataverse test if exists;
+
+create dataverse test;
+
+use test;
+
+create type FooType as {
+  fid: int32
+}
+
+create type BarType as {
+  bid: int32
+}
+
+create dataset Foo(FooType) primary key fid;
+
+create dataset Bar(BarType) primary key bid;
+
+SELECT * FROM Bar b
+WHERE b.fee < (SELECT VALUE AVG(f.fee) FROM Foo f)[0];
+
+drop dataverse test;

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-app/src/test/resources/optimizerts/queries/tpcds/query-ASTERIXDB-1581-correlated.sqlpp
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/tpcds/query-ASTERIXDB-1581-correlated.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/tpcds/query-ASTERIXDB-1581-correlated.sqlpp
new file mode 100644
index 0000000..9f96f9b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/tpcds/query-ASTERIXDB-1581-correlated.sqlpp
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+drop dataverse tpcds if exists;
+create dataverse tpcds;
+
+use tpcds;
+
+create type tpcds.store_sales_type as closed {
+    ss_sold_date_sk:           int64?,
+    ss_sold_time_sk:           int64?,
+    ss_item_sk:                int64,
+    ss_customer_sk:            int64?,
+    ss_cdemo_sk:               int64?,
+    ss_hdemo_sk:               int64?,
+    ss_addr_sk:                int64?,
+    ss_store_sk:               int64?,
+    ss_promo_sk:               int64?,
+    ss_ticket_number:          int64,
+    ss_quantity:               int64?,
+    ss_wholesale_cost:         double?,
+    ss_list_price:             double?,
+    ss_sales_price:            double?,
+    ss_ext_discount_amt:       double?,
+    ss_ext_sales_price:        double?,
+    ss_ext_wholesale_cost:     double?,
+    ss_ext_list_price:         double?,
+    ss_ext_tax:                double?,
+    ss_coupon_amt:             double?,
+    ss_net_paid:               double?,
+    ss_net_paid_inc_tax:       double?,
+    ss_net_profit:             double?
+}
+
+
+create type tpcds.item_type as closed {
+    i_item_sk:                 int64,
+    i_item_id:                 string,
+    i_rec_start_date:          string?,
+    i_rec_end_date:            string?,
+    i_item_desc:               string?,
+    i_current_price:           double?,
+    i_wholesale_cost:          double?,
+    i_brand_id:                int64? ,
+    i_brand:                   string?,
+    i_class_id:                int64? ,
+    i_class:                   string?,
+    i_category_id:             int64? ,
+    i_category:                string?,
+    i_manufact_id:             int64? ,
+    i_manufact:                string?,
+    i_size:                    string?,
+    i_formulation:             string?,
+    i_color:                   string?,
+    i_units:                   string?,
+    i_container:               string?,
+    i_manager_id:              int64?,
+    i_product_name:            string?
+}
+
+create dataset store_sales (store_sales_type)
+primary key ss_item_sk, ss_ticket_number;
+
+create dataset item (item_type)
+primary key i_item_sk;
+
+select case when (select value count(ss)
+                  from store_sales ss
+                  where ss_quantity = item.i_item_sk)[0] < 25437
+            then (select avg(ss_ext_discount_amt)
+                  from store_sales
+                  where ss_quantity = item.i_item_sk)
+            else (select avg(ss_net_profit)
+                  from store_sales
+                  where ss_quantity = item.i_item_sk)
+            end bucket1
+from item
+where i_item_sk = 1;
+
+drop dataverse tpcds;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-app/src/test/resources/optimizerts/queries/tpcds/query-ASTERIXDB-1581.sqlpp
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/tpcds/query-ASTERIXDB-1581.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/tpcds/query-ASTERIXDB-1581.sqlpp
new file mode 100644
index 0000000..1fecc6c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/tpcds/query-ASTERIXDB-1581.sqlpp
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+drop dataverse tpcds if exists;
+create dataverse tpcds;
+
+use tpcds;
+
+create type tpcds.store_sales_type as closed {
+    ss_sold_date_sk:           int64?,
+    ss_sold_time_sk:           int64?,
+    ss_item_sk:                int64,
+    ss_customer_sk:            int64?,
+    ss_cdemo_sk:               int64?,
+    ss_hdemo_sk:               int64?,
+    ss_addr_sk:                int64?,
+    ss_store_sk:               int64?,
+    ss_promo_sk:               int64?,
+    ss_ticket_number:          int64,
+    ss_quantity:               int64?,
+    ss_wholesale_cost:         double?,
+    ss_list_price:             double?,
+    ss_sales_price:            double?,
+    ss_ext_discount_amt:       double?,
+    ss_ext_sales_price:        double?,
+    ss_ext_wholesale_cost:     double?,
+    ss_ext_list_price:         double?,
+    ss_ext_tax:                double?,
+    ss_coupon_amt:             double?,
+    ss_net_paid:               double?,
+    ss_net_paid_inc_tax:       double?,
+    ss_net_profit:             double?
+}
+
+
+create type tpcds.item_type as closed {
+    i_item_sk:                 int64,
+    i_item_id:                 string,
+    i_rec_start_date:          string?,
+    i_rec_end_date:            string?,
+    i_item_desc:               string?,
+    i_current_price:           double?,
+    i_wholesale_cost:          double?,
+    i_brand_id:                int64? ,
+    i_brand:                   string?,
+    i_class_id:                int64? ,
+    i_class:                   string?,
+    i_category_id:             int64? ,
+    i_category:                string?,
+    i_manufact_id:             int64? ,
+    i_manufact:                string?,
+    i_size:                    string?,
+    i_formulation:             string?,
+    i_color:                   string?,
+    i_units:                   string?,
+    i_container:               string?,
+    i_manager_id:              int64?,
+    i_product_name:            string?
+}
+
+create dataset store_sales (store_sales_type)
+primary key ss_item_sk, ss_ticket_number;
+
+create dataset item (item_type)
+primary key i_item_sk;
+
+select case when (select value count(ss)
+                  from store_sales ss
+                  where ss_quantity >= 1 and ss_quantity <= 20)[0] < 25437
+            then (select avg(ss_ext_discount_amt)
+                  from store_sales
+                  where ss_quantity >= 1 and ss_quantity <= 20)
+            else (select avg(ss_net_profit)
+                  from store_sales
+                  where ss_quantity >= 1 and ss_quantity <= 20)
+            end bucket1
+from item
+where i_item_sk = 1;
+
+drop dataverse tpcds;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-app/src/test/resources/optimizerts/results/filter-nested.plan
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/filter-nested.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/filter-nested.plan
index 7448380..3ed2b6f 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results/filter-nested.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/filter-nested.plan
@@ -6,6 +6,6 @@
           -- STREAM_PROJECT  |PARTITIONED|
             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
               -- DATASOURCE_SCAN  |PARTITIONED|
-                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- BROADCAST_EXCHANGE  |PARTITIONED|
                   -- ASSIGN  |PARTITIONED|
-                    -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
\ No newline at end of file
+                    -- EMPTY_TUPLE_SOURCE  |PARTITIONED|

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/c89d668f/asterixdb/asterix-app/src/test/resources/optimizerts/results/join-singletonbranch-2.plan
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/join-singletonbranch-2.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/join-singletonbranch-2.plan
new file mode 100644
index 0000000..3bb60f3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/join-singletonbranch-2.plan
@@ -0,0 +1,28 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- STREAM_PROJECT  |PARTITIONED|
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            -- NESTED_LOOP  |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|
+              -- BROADCAST_EXCHANGE  |PARTITIONED|
+                -- ASSIGN  |UNPARTITIONED|
+                  -- STREAM_PROJECT  |UNPARTITIONED|
+                    -- ASSIGN  |UNPARTITIONED|
+                      -- AGGREGATE  |UNPARTITIONED|
+                        -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
+                          -- AGGREGATE  |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/c89d668f/asterixdb/asterix-app/src/test/resources/optimizerts/results/join-singletonbranch-3.plan
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/join-singletonbranch-3.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/join-singletonbranch-3.plan
new file mode 100644
index 0000000..7b5a4ee
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/join-singletonbranch-3.plan
@@ -0,0 +1,23 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- STREAM_PROJECT  |PARTITIONED|
+          -- STREAM_SELECT  |PARTITIONED|
+            -- STREAM_PROJECT  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- DATASOURCE_SCAN  |PARTITIONED|
+                  -- BROADCAST_EXCHANGE  |PARTITIONED|
+                    -- ASSIGN  |UNPARTITIONED|
+                      -- STREAM_PROJECT  |UNPARTITIONED|
+                        -- ASSIGN  |UNPARTITIONED|
+                          -- AGGREGATE  |UNPARTITIONED|
+                            -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
+                              -- AGGREGATE  |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/c89d668f/asterixdb/asterix-app/src/test/resources/optimizerts/results/join-singletonbranch.plan
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/join-singletonbranch.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/join-singletonbranch.plan
new file mode 100644
index 0000000..c072812
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/join-singletonbranch.plan
@@ -0,0 +1,28 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- STREAM_PROJECT  |PARTITIONED|
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            -- NESTED_LOOP  |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|
+              -- BROADCAST_EXCHANGE  |PARTITIONED|
+                -- STREAM_PROJECT  |UNPARTITIONED|
+                  -- ASSIGN  |UNPARTITIONED|
+                    -- AGGREGATE  |UNPARTITIONED|
+                      -- AGGREGATE  |UNPARTITIONED|
+                        -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
+                          -- AGGREGATE  |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/c89d668f/asterixdb/asterix-app/src/test/resources/optimizerts/results/nest_aggregate.plan
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/nest_aggregate.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/nest_aggregate.plan
index 41b07d9..d4cdff4 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results/nest_aggregate.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/nest_aggregate.plan
@@ -8,7 +8,7 @@
                     {
                       -- AGGREGATE  |LOCAL|
                         -- STREAM_LIMIT  |LOCAL|
-                          -- IN_MEMORY_STABLE_SORT [$$41(ASC)]  |LOCAL|
+                          -- IN_MEMORY_STABLE_SORT [$$39(ASC)]  |LOCAL|
                             -- MICRO_PRE_CLUSTERED_GROUP_BY[$$51]  |LOCAL|
                                     {
                                       -- AGGREGATE  |LOCAL|
@@ -29,7 +29,7 @@
                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                         -- STREAM_PROJECT  |PARTITIONED|
                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                            -- HYBRID_HASH_JOIN [$$34][$$39]  |PARTITIONED|
+                            -- HYBRID_HASH_JOIN [$$34][$$40]  |PARTITIONED|
                               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                 -- HYBRID_HASH_JOIN [$$34][$$35]  |PARTITIONED|
                                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
@@ -45,7 +45,7 @@
                                         -- DATASOURCE_SCAN  |PARTITIONED|
                                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                             -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                              -- HASH_PARTITION_EXCHANGE [$$39]  |PARTITIONED|
+                              -- HASH_PARTITION_EXCHANGE [$$40]  |PARTITIONED|
                                 -- ASSIGN  |PARTITIONED|
                                   -- STREAM_PROJECT  |PARTITIONED|
                                     -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|