[ASTERIXDB-3555][COMP] Use Join Samples to get Join Selectivity

Ext-ref: MB-65101

Change-Id: I863dc378d5ead9f8b48f09f7bc400f5bdaba4839
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/19383
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
Reviewed-by: <murali.krishna@couchbase.com>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Michael Blow <mblow@apache.org>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinCondition.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinCondition.java
index b5c290f..3ef1bea 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinCondition.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinCondition.java
@@ -39,6 +39,7 @@
     protected int rightSideBits;
     protected double selectivity;
     protected comparisonOp comparisonType;
+    protected JoinOperator joinOp;
 
     protected enum comparisonOp {
         OP_EQ,
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java
index c8675df..0670fb8 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java
@@ -445,7 +445,6 @@
     // This finds all the join Conditions in the whole query. This is a global list of all join predicates.
     // It also fills in the dataset Bits for each join predicate.
     private void findJoinConditionsAndAssignSels() throws AlgebricksException {
-
         List<Mutable<ILogicalExpression>> conjs = new ArrayList<>();
         for (JoinOperator jOp : allJoinOps) {
             AbstractBinaryJoinOperator joinOp = jOp.getAbstractJoinOp();
@@ -461,6 +460,7 @@
                     }
                     jc.joinCondition = conj.getValue().cloneExpression();
                     joinConditions.add(jc);
+                    jc.joinOp = jOp;
                 }
             } else {
                 if ((expr.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL)) {
@@ -472,6 +472,7 @@
                     // change to not a true condition
                     jc.joinCondition = expr.cloneExpression();
                     joinConditions.add(jc);
+                    jc.joinOp = jOp;
                 }
             }
         }
@@ -493,7 +494,7 @@
                     }
                 }
             }
-            jc.selectivity = stats.getSelectivityFromAnnotationMain(jc.joinCondition, true, false);
+            jc.selectivity = stats.getSelectivityFromAnnotationMain(jc.joinCondition, true, false, jc.joinOp);
         }
         for (int i = erase.size() - 1; i >= 0; i--) {
             assignOps.remove(erase.get(i));
@@ -1093,7 +1094,7 @@
         if (this.singleDatasetPreds.size() > 0) { // We did not have selectivities for these before. Now we do.
             for (JoinCondition jc : joinConditions) {
                 // we may be repeating some work here, but that is ok. This will rarely happen (happens in q7 tpch)
-                double sel = stats.getSelectivityFromAnnotationMain(jc.getJoinCondition(), false, true);
+                double sel = stats.getSelectivityFromAnnotationMain(jc.getJoinCondition(), false, true, null);
                 if (sel != -1) {
                     jc.selectivity = sel;
                 }
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/Stats.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/Stats.java
index 1cc643a..52566ef 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/Stats.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/Stats.java
@@ -46,6 +46,7 @@
 import org.apache.commons.lang3.mutable.Mutable;
 import org.apache.commons.lang3.mutable.MutableObject;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.common.utils.Pair;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
 import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
@@ -61,6 +62,7 @@
 import org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
 import org.apache.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
+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.AggregateOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
@@ -112,8 +114,8 @@
         return mdp.findSampleIndex(dsid.getDatabaseName(), dsid.getDataverseName(), dsid.getDatasourceName());
     }
 
-    private double findJoinSelectivity(JoinProductivityAnnotation anno, AbstractFunctionCallExpression joinExpr)
-            throws AlgebricksException {
+    private double findJoinSelectivity(JoinProductivityAnnotation anno, AbstractFunctionCallExpression joinExpr,
+            JoinOperator jOp) throws AlgebricksException {
         List<LogicalVariable> exprUsedVars = new ArrayList<>();
         joinExpr.getUsedVariables(exprUsedVars);
         if (exprUsedVars.size() != 2) {
@@ -172,60 +174,121 @@
                 return productivity / card1;
             }
         } else {
-            ILogicalOperator leafInput;
-            LogicalVariable var;
-            if (!(joinExpr.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.EQ))) {
-                return 0.5; // we will assume half; rest of the code assumes EQ joins
+            Index index1 = findIndex(joinEnum.leafInputs.get(idx1 - 1));
+            if (index1 == null) {
+                return 0.5;
             }
-            // choose the smaller side sample; better results this way for sure!
-            if (card1 < card2) {
-                leafInput = joinEnum.leafInputs.get(idx1 - 1);
-                var = exprUsedVars.get(0);
-            } else {
-                leafInput = joinEnum.leafInputs.get(idx2 - 1);
-                var = exprUsedVars.get(1);
-            }
-            Index index = findIndex(leafInput);
-            if (index == null) {
-                return 1.0;
-            }
-            List<List<IAObject>> result = runSamplingQueryDistinct(this.optCtx, leafInput, var, index);
-            if (result == null) {
-                return 1.0;
+            Index index2 = findIndex(joinEnum.leafInputs.get(idx2 - 1));
+            if (index2 == null) {
+                return 0.5;
             }
 
-            double estDistinctCardinalityFromSample = findPredicateCardinality(result, true);
-            if (estDistinctCardinalityFromSample == 0) {
-                estDistinctCardinalityFromSample = 1; // just in case
-            }
-            Index.SampleIndexDetails details = (Index.SampleIndexDetails) index.getIndexDetails();
-            double numDistincts;
-            // if the table is smaller than the sample size, there is no need to use the estimator
-            //                                            getSampleCardinalityTarget() equals 1063 or 4252 or 17008
-            if (details.getSourceCardinality() <= details.getSampleCardinalityTarget()) {
-                numDistincts = estDistinctCardinalityFromSample;
-            } else { // when the number of distincts is smaller than approx 25% of the sample size, then we do not
-                         // then we do not need to call the estimator. This is a good heuristic. This was obtained by looking at the graph
-                     // of d = D ( 1 - e^(-getSampleCardinalityTarget/D) ; d = estDistinctCardinalityFromSample; D = actual number of distincts
-                if (estDistinctCardinalityFromSample <= 0.25 * details.getSampleCardinalityTarget()) {
-                    numDistincts = estDistinctCardinalityFromSample;
-                } else {
-                    numDistincts = secondDistinctEstimator(estDistinctCardinalityFromSample, index);
+            if (!(joinExpr.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.EQ))) {
+                return 0.5; // this may not be accurate obviously!
+            } // we can do all relops here and other joins such as interval joins and spatial joins, the compile time might increase a lot
+
+            Index.SampleIndexDetails idxDetails1 = (Index.SampleIndexDetails) index1.getIndexDetails();
+            Index.SampleIndexDetails idxDetails2 = (Index.SampleIndexDetails) index2.getIndexDetails();
+            if ((idxDetails1.getSourceCardinality() < idxDetails1.getSampleCardinalityTarget())
+                    || (idxDetails2.getSourceCardinality() < idxDetails2.getSampleCardinalityTarget())) {
+                double sel = findJoinSelFromSamples(joinEnum.leafInputs.get(idx1 - 1),
+                        joinEnum.leafInputs.get(idx2 - 1), index1, index2, joinExpr, jOp);
+                if (sel > 0.0) { // if sel is 0.0 we call naiveJoinSelectivity
+                    return sel;
                 }
             }
-            if (numDistincts > details.getSourceCardinality()) {
-                numDistincts = details.getSourceCardinality(); // cannot exceed table cardinality
-            }
-            return 1.0 / numDistincts; // this is the expected selectivity for joins for Fk-PK and Fk-Fk joins
+            // Now we can handle only equi joins. We make all the uniform and independence assumptions here.
+            double sel = naiveJoinSelectivity(exprUsedVars, card1, card2, idx1, idx2);
+            return sel;
         }
     }
 
+    private double naiveJoinSelectivity(List<LogicalVariable> exprUsedVars, double card1, double card2, int idx1,
+            int idx2) throws AlgebricksException {
+        ILogicalOperator leafInput;
+        LogicalVariable var;
+
+        // choose the smaller side sample; better results this way for sure!
+        if (card1 < card2) {
+            leafInput = joinEnum.leafInputs.get(idx1 - 1);
+            var = exprUsedVars.get(0);
+        } else {
+            leafInput = joinEnum.leafInputs.get(idx2 - 1);
+            var = exprUsedVars.get(1);
+        }
+        Index index = findIndex(leafInput);
+        if (index == null) {
+            return 1.0;
+        }
+        List<List<IAObject>> result = runSamplingQueryDistinct(this.optCtx, leafInput, var, index);
+        if (result == null) {
+            return 1.0;
+        }
+
+        double estDistinctCardinalityFromSample = findPredicateCardinality(result, true);
+        if (estDistinctCardinalityFromSample == 0) {
+            estDistinctCardinalityFromSample = 1; // just in case
+        }
+        Index.SampleIndexDetails details = (Index.SampleIndexDetails) index.getIndexDetails();
+        double numDistincts;
+        // if the table is smaller than the sample size, there is no need to use the estimator
+        //                                            getSampleCardinalityTarget() equals 1063 or 4252 or 17008
+        if (details.getSourceCardinality() <= details.getSampleCardinalityTarget()) {
+            numDistincts = estDistinctCardinalityFromSample;
+        } else { // when the number of distincts is smaller than approx 25% of the sample size, then we do not
+            // then we do not need to call the estimator. This is a good heuristic. This was obtained by looking at the graph
+            // of d = D ( 1 - e^(-getSampleCardinalityTarget/D) ; d = estDistinctCardinalityFromSample; D = actual number of distincts
+            if (estDistinctCardinalityFromSample <= 0.25 * details.getSampleCardinalityTarget()) {
+                numDistincts = estDistinctCardinalityFromSample;
+            } else {
+                numDistincts = secondDistinctEstimator(estDistinctCardinalityFromSample, index);
+            }
+        }
+        if (numDistincts > details.getSourceCardinality()) {
+            numDistincts = details.getSourceCardinality(); // cannot exceed table cardinality
+        }
+        return 1.0 / numDistincts; // this is the expected selectivity for joins for Fk-PK and Fk-Fk joins
+    }
+
+    private double findJoinSelFromSamples(ILogicalOperator left, ILogicalOperator right, Index index1, Index index2,
+            AbstractFunctionCallExpression joinExpr, JoinOperator join) throws AlgebricksException {
+        AbstractBinaryJoinOperator abjoin = join.getAbstractJoinOp();
+        Pair<ILogicalOperator, Double> leftOutput = replaceDataSourceWithSample(left, index1);
+        abjoin.getInputs().get(0).setValue(leftOutput.getFirst());
+        Pair<ILogicalOperator, Double> rightOutput = replaceDataSourceWithSample(right, index2);
+        abjoin.getInputs().get(1).setValue(rightOutput.getFirst());
+        abjoin.getCondition().setValue(joinExpr);
+        List<List<IAObject>> result = runSamplingQuery(optCtx, abjoin);
+        double estCardSample = findPredicateCardinality(result, false);
+        double sel = estCardSample / leftOutput.getSecond() / rightOutput.getSecond();
+        return sel;
+    }
+
+    private Pair<ILogicalOperator, Double> replaceDataSourceWithSample(ILogicalOperator op, Index index)
+            throws AlgebricksException {
+        ILogicalOperator selOp = OperatorManipulationUtil.bottomUpCopyOperators(op);
+        // must set all the Sel operators to be true, otherwise we will be multiplying the single table sels also here.
+        storeSelectConditionsAndMakeThemTrue(selOp, null);
+        ILogicalOperator parent = joinEnum.findDataSourceScanOperatorParent(selOp);
+        DataSourceScanOperator scanOp = (DataSourceScanOperator) parent.getInputs().get(0).getValue();
+        Index.SampleIndexDetails idxDetails = (Index.SampleIndexDetails) index.getIndexDetails();
+        double origDatasetCard = idxDetails.getSourceCardinality();
+        double sampleCard = Math.min(idxDetails.getSampleCardinalityTarget(), origDatasetCard);
+
+        // replace the dataScanSourceOperator with the sampling source
+        SampleDataSource sampledatasource = joinEnum.getSampleDataSource(scanOp);
+        scanOp.setDataSource(sampledatasource);
+        parent.getInputs().get(0).setValue(scanOp);
+        Pair<ILogicalOperator, Double> retVal = new Pair<>(selOp, sampleCard);
+
+        return retVal;
+    }
+
     // The expression we get may not be a base condition. It could be comprised of ors and ands and nots. So have to
     //recursively find the overall selectivity.
     private double getSelectivityFromAnnotation(AbstractFunctionCallExpression afcExpr, boolean join,
-            boolean singleDatasetPreds) throws AlgebricksException {
+            boolean singleDatasetPreds, JoinOperator jOp) throws AlgebricksException {
         double sel = 1.0;
-
         if (afcExpr.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.OR)) {
             double orSel = 0.0;
             for (int i = 0; i < afcExpr.getArguments().size(); i++) {
@@ -233,7 +296,7 @@
                 if (lexpr.getExpressionTag().equals(LogicalExpressionTag.FUNCTION_CALL)) {
                     sel = getSelectivityFromAnnotation(
                             (AbstractFunctionCallExpression) afcExpr.getArguments().get(i).getValue(), join,
-                            singleDatasetPreds);
+                            singleDatasetPreds, jOp);
                     orSel = orSel + sel - orSel * sel;
                 }
             }
@@ -245,7 +308,7 @@
                 if (lexpr.getExpressionTag().equals(LogicalExpressionTag.FUNCTION_CALL)) {
                     sel = getSelectivityFromAnnotation(
                             (AbstractFunctionCallExpression) afcExpr.getArguments().get(i).getValue(), join,
-                            singleDatasetPreds);
+                            singleDatasetPreds, jOp);
                     andSel *= sel;
                 }
             }
@@ -255,7 +318,7 @@
             if (lexpr.getExpressionTag().equals(LogicalExpressionTag.FUNCTION_CALL)) {
                 sel = getSelectivityFromAnnotation(
                         (AbstractFunctionCallExpression) afcExpr.getArguments().get(0).getValue(), join,
-                        singleDatasetPreds);
+                        singleDatasetPreds, jOp);
                 // We want to return 1.0 and not 0.0 if there was no annotation
                 return (sel == 1.0) ? 1.0 : 1.0 - sel;
             }
@@ -284,7 +347,7 @@
             }
         } else {
             JoinProductivityAnnotation jpa = afcExpr.getAnnotation(JoinProductivityAnnotation.class);
-            s = findJoinSelectivity(jpa, afcExpr);
+            s = findJoinSelectivity(jpa, afcExpr, jOp);
             sel *= s;
         }
 
@@ -299,12 +362,12 @@
     }
 
     protected double getSelectivityFromAnnotationMain(ILogicalExpression leExpr, boolean join,
-            boolean singleDatasetPreds) throws AlgebricksException {
+            boolean singleDatasetPreds, JoinOperator jOp) throws AlgebricksException {
         double sel = 1.0;
 
         if (leExpr.getExpressionTag().equals(LogicalExpressionTag.FUNCTION_CALL)) {
             AbstractFunctionCallExpression afcExpr = (AbstractFunctionCallExpression) leExpr;
-            sel = getSelectivityFromAnnotation(afcExpr, join, singleDatasetPreds);
+            sel = getSelectivityFromAnnotation(afcExpr, join, singleDatasetPreds, jOp);
         }
 
         return sel;
@@ -322,7 +385,7 @@
         while (op.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
             if (op.getOperatorTag() == LogicalOperatorTag.SELECT) {
                 SelectOperator selOper = (SelectOperator) op;
-                sel *= getSelectivityFromAnnotationMain(selOper.getCondition().getValue(), join, false);
+                sel *= getSelectivityFromAnnotationMain(selOper.getCondition().getValue(), join, false, null);
             }
             if (op.getOperatorTag() == LogicalOperatorTag.SUBPLAN) {
                 sel *= getSelectivity((SubplanOperator) op);
@@ -339,7 +402,7 @@
         while (true) {
             if (op.getOperatorTag() == LogicalOperatorTag.SELECT) {
                 SelectOperator selOper = (SelectOperator) op;
-                sel *= getSelectivityFromAnnotationMain(selOper.getCondition().getValue(), false, false);
+                sel *= getSelectivityFromAnnotationMain(selOper.getCondition().getValue(), false, false, null);
             }
             if (op.getInputs().size() > 0) {
                 op = op.getInputs().get(0).getValue();
@@ -677,6 +740,10 @@
         if (index == null) {
             return null;
         }
+        Index.SampleIndexDetails idxDetails = (Index.SampleIndexDetails) index.getIndexDetails();
+        double origDatasetCard = idxDetails.getSourceCardinality();
+        double sampleCard = Math.min(idxDetails.getSampleCardinalityTarget(), origDatasetCard);
+        issueWarning(sampleCard, scanOp);
         return index;
     }
 
@@ -743,9 +810,6 @@
             scanOp = (DataSourceScanOperator) parent.getInputs().get(0).getValue();
         }
         Index.SampleIndexDetails idxDetails = (Index.SampleIndexDetails) index.getIndexDetails();
-        double origDatasetCard = idxDetails.getSourceCardinality();
-        double sampleCard = Math.min(idxDetails.getSampleCardinalityTarget(), origDatasetCard);
-        issueWarning(sampleCard, scanOp);
 
         // replace the dataScanSourceOperator with the sampling source
         SampleDataSource sampledatasource = joinEnum.getSampleDataSource(scanOp);
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/ch2/ch2_q7.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/ch2/ch2_q7.plan
index 0334a38..9296e8d 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/ch2/ch2_q7.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/ch2/ch2_q7.plan
@@ -18,17 +18,17 @@
                   -- STREAM_SELECT  |PARTITIONED|
                     -- STREAM_PROJECT  |PARTITIONED|
                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                        -- HYBRID_HASH_JOIN [$$288][$$304]  |PARTITIONED|
+                        -- HYBRID_HASH_JOIN [$$277][$$303]  |PARTITIONED|
                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                             -- STREAM_PROJECT  |PARTITIONED|
                               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                -- HYBRID_HASH_JOIN [$$277][$$303]  |PARTITIONED|
+                                -- HYBRID_HASH_JOIN [$$326][$$300]  |PARTITIONED|
                                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                    -- STREAM_PROJECT  |PARTITIONED|
-                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                        -- HYBRID_HASH_JOIN [$$326][$$300]  |PARTITIONED|
-                                          -- HASH_PARTITION_EXCHANGE [$$326]  |PARTITIONED|
-                                            -- ASSIGN  |PARTITIONED|
+                                    -- ASSIGN  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- HYBRID_HASH_JOIN [$$288][$$304]  |PARTITIONED|
+                                            -- HASH_PARTITION_EXCHANGE [$$288]  |PARTITIONED|
                                               -- STREAM_PROJECT  |PARTITIONED|
                                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                                   -- HYBRID_HASH_JOIN [$$291, $$293, $$295][$$305, $$306, $$307]  |PARTITIONED|
@@ -61,27 +61,27 @@
                                                               -- DATASOURCE_SCAN (test.customer)  |PARTITIONED|
                                                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                                                   -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                                          -- HASH_PARTITION_EXCHANGE [$$300]  |PARTITIONED|
-                                            -- ASSIGN  |PARTITIONED|
-                                              -- STREAM_PROJECT  |PARTITIONED|
+                                            -- HASH_PARTITION_EXCHANGE [$$304]  |PARTITIONED|
+                                              -- REPLICATE  |PARTITIONED|
                                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                                  -- DATASOURCE_SCAN (test.supplier)  |PARTITIONED|
-                                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                    -- REPLICATE  |PARTITIONED|
-                                      -- BROADCAST_EXCHANGE  |PARTITIONED|
-                                        -- ASSIGN  |PARTITIONED|
-                                          -- STREAM_PROJECT  |PARTITIONED|
+                                                  -- ASSIGN  |PARTITIONED|
+                                                    -- STREAM_PROJECT  |PARTITIONED|
+                                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                        -- DATASOURCE_SCAN (test.nation)  |PARTITIONED|
+                                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                  -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                    -- ASSIGN  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- DATASOURCE_SCAN (test.supplier)  |PARTITIONED|
                                             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                              -- DATASOURCE_SCAN (test.nation)  |PARTITIONED|
-                                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                                  -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                          -- BROADCAST_EXCHANGE  |PARTITIONED|
                             -- ASSIGN  |PARTITIONED|
                               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                 -- REPLICATE  |PARTITIONED|
-                                  -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                     -- ASSIGN  |PARTITIONED|
                                       -- STREAM_PROJECT  |PARTITIONED|
                                         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/hash-join-with-redundant-variable/hash-join-with-redundant-variable.1.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/hash-join-with-redundant-variable/hash-join-with-redundant-variable.1.plan
index 310ef7c..9e8ccb3 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/hash-join-with-redundant-variable/hash-join-with-redundant-variable.1.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/hash-join-with-redundant-variable/hash-join-with-redundant-variable.1.plan
@@ -4,64 +4,64 @@
       -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
         -- AGGREGATE  |PARTITIONED|
           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-            -- HYBRID_HASH_JOIN [$$134, $$124][$$135, $$123]  |PARTITIONED|
+            -- HYBRID_HASH_JOIN [$$136][$$137]  |PARTITIONED|
               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                 -- STREAM_PROJECT  |PARTITIONED|
                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                    -- HYBRID_HASH_JOIN [$$139][$$138]  |PARTITIONED|
+                    -- HYBRID_HASH_JOIN [$$123][$$124]  |PARTITIONED|
                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                         -- STREAM_PROJECT  |PARTITIONED|
                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                            -- HYBRID_HASH_JOIN [$$132][$$133]  |PARTITIONED|
+                            -- HYBRID_HASH_JOIN [$$134, $$122][$$135, $$123]  |PARTITIONED|
                               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                 -- STREAM_PROJECT  |PARTITIONED|
                                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                    -- HYBRID_HASH_JOIN [$$124][$$122]  |PARTITIONED|
+                                    -- HYBRID_HASH_JOIN [$$139][$$138]  |PARTITIONED|
                                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                         -- STREAM_PROJECT  |PARTITIONED|
                                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                            -- HYBRID_HASH_JOIN [$$137][$$136]  |PARTITIONED|
-                                              -- HASH_PARTITION_EXCHANGE [$$137]  |PARTITIONED|
+                                            -- HYBRID_HASH_JOIN [$$132][$$133]  |PARTITIONED|
+                                              -- HASH_PARTITION_EXCHANGE [$$132]  |PARTITIONED|
+                                                -- ASSIGN  |PARTITIONED|
+                                                  -- STREAM_PROJECT  |PARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                      -- DATASOURCE_SCAN (test.customer)  |PARTITIONED|
+                                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                              -- HASH_PARTITION_EXCHANGE [$$133]  |PARTITIONED|
                                                 -- STREAM_SELECT  |PARTITIONED|
                                                   -- ASSIGN  |PARTITIONED|
                                                     -- STREAM_PROJECT  |PARTITIONED|
                                                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                                        -- DATASOURCE_SCAN (test.region)  |PARTITIONED|
+                                                        -- DATASOURCE_SCAN (test.orders)  |PARTITIONED|
                                                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                                             -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                                              -- HASH_PARTITION_EXCHANGE [$$136]  |PARTITIONED|
-                                                -- ASSIGN  |PARTITIONED|
-                                                  -- STREAM_PROJECT  |PARTITIONED|
-                                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                                      -- DATASOURCE_SCAN (test.nation)  |PARTITIONED|
-                                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
                                       -- BROADCAST_EXCHANGE  |PARTITIONED|
                                         -- ASSIGN  |PARTITIONED|
                                           -- STREAM_PROJECT  |PARTITIONED|
                                             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                              -- DATASOURCE_SCAN (test.customer)  |PARTITIONED|
+                                              -- DATASOURCE_SCAN (test.lineitem)  |PARTITIONED|
                                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                                   -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
                               -- BROADCAST_EXCHANGE  |PARTITIONED|
-                                -- STREAM_SELECT  |PARTITIONED|
-                                  -- ASSIGN  |PARTITIONED|
-                                    -- STREAM_PROJECT  |PARTITIONED|
-                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                        -- DATASOURCE_SCAN (test.orders)  |PARTITIONED|
-                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- DATASOURCE_SCAN (test.supplier)  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
                       -- BROADCAST_EXCHANGE  |PARTITIONED|
                         -- ASSIGN  |PARTITIONED|
                           -- STREAM_PROJECT  |PARTITIONED|
                             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                              -- DATASOURCE_SCAN (test.lineitem)  |PARTITIONED|
+                              -- DATASOURCE_SCAN (test.nation)  |PARTITIONED|
                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                   -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
               -- BROADCAST_EXCHANGE  |PARTITIONED|
-                -- ASSIGN  |PARTITIONED|
-                  -- STREAM_PROJECT  |PARTITIONED|
-                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                      -- DATASOURCE_SCAN (test.supplier)  |PARTITIONED|
-                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                -- STREAM_SELECT  |PARTITIONED|
+                  -- ASSIGN  |PARTITIONED|
+                    -- STREAM_PROJECT  |PARTITIONED|
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        -- DATASOURCE_SCAN (test.region)  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.5.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.5.plan
index 4d11aca..05c57bd 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.5.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.5.plan
@@ -1,28 +1,28 @@
-distribute result [$$101] [cardinality: 1003.05, op-cost: 0.0, total-cost: 18041.34]
+distribute result [$$101] [cardinality: 1003.97, op-cost: 0.0, total-cost: 18052.66]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 1003.05, op-cost: 0.0, total-cost: 18041.34]
+  exchange [cardinality: 1003.97, op-cost: 0.0, total-cost: 18052.66]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$101] <- [{"$1": $$111}] project: [$$101] [cardinality: 1003.05, op-cost: 0.0, total-cost: 18041.34]
+    assign [$$101] <- [{"$1": $$111}] project: [$$101] [cardinality: 1003.97, op-cost: 0.0, total-cost: 18052.66]
     -- ASSIGN  |PARTITIONED|
-      project ([$$111]) [cardinality: 1003.05, op-cost: 0.0, total-cost: 18041.34]
+      project ([$$111]) [cardinality: 1003.97, op-cost: 0.0, total-cost: 18052.66]
       -- STREAM_PROJECT  |PARTITIONED|
-        exchange [cardinality: 1003.05, op-cost: 0.0, total-cost: 18041.34]
+        exchange [cardinality: 1003.97, op-cost: 0.0, total-cost: 18052.66]
         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
           group by ([$$l_partkey := $$115; $$o_orderstatus := $$116; $$c_nationkey := $$117]) decor ([]) {
                     aggregate [$$111] <- [sql-sum-serial($$114)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                     -- AGGREGATE  |LOCAL|
                       nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                       -- NESTED_TUPLE_SOURCE  |LOCAL|
-                 } [cardinality: 1003.05, op-cost: 6018.17, total-cost: 18041.34]
+                 } [cardinality: 1003.97, op-cost: 6023.83, total-cost: 18052.66]
           -- EXTERNAL_GROUP_BY[$$115, $$116, $$117]  |PARTITIONED|
-            exchange [cardinality: 1003.05, op-cost: 0.0, total-cost: 12023.17]
+            exchange [cardinality: 1003.97, op-cost: 0.0, total-cost: 12028.83]
             -- HASH_PARTITION_EXCHANGE [$$115, $$116, $$117]  |PARTITIONED|
               group by ([$$115 := $$102; $$116 := $$103; $$117 := $$104]) decor ([]) {
                         aggregate [$$114] <- [sql-count-serial(1)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                         -- AGGREGATE  |LOCAL|
                           nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                           -- NESTED_TUPLE_SOURCE  |LOCAL|
-                     } [cardinality: 1003.05, op-cost: 6018.17, total-cost: 12023.17]
+                     } [cardinality: 1003.97, op-cost: 6023.83, total-cost: 12028.83]
               -- EXTERNAL_GROUP_BY[$$102, $$103, $$104]  |PARTITIONED|
                 exchange [cardinality: 6010.65, op-cost: 0.0, total-cost: 6005.0]
                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
@@ -40,7 +40,7 @@
                             -- STREAM_PROJECT  |PARTITIONED|
                               exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                join (eq($$109, $$108)) [cardinality: 1501.41, op-cost: 2101.41, total-cost: 4351.41]
+                                join (eq($$109, $$108)) [cardinality: 1502.82, op-cost: 2101.41, total-cost: 4351.41]
                                 -- HYBRID_HASH_JOIN [$$109][$$108]  |PARTITIONED|
                                   exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.6.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.6.plan
index 743ec55..3d92aab 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.6.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.6.plan
@@ -1,36 +1,36 @@
-distribute result [$$101] [cardinality: 1003.05, op-cost: 0.0, total-cost: 34411.56]
+distribute result [$$101] [cardinality: 1003.97, op-cost: 0.0, total-cost: 34439.83]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 1003.05, op-cost: 0.0, total-cost: 34411.56]
+  exchange [cardinality: 1003.97, op-cost: 0.0, total-cost: 34439.83]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$101] <- [{"$1": $$111}] project: [$$101] [cardinality: 1003.05, op-cost: 0.0, total-cost: 34411.56]
+    assign [$$101] <- [{"$1": $$111}] project: [$$101] [cardinality: 1003.97, op-cost: 0.0, total-cost: 34439.83]
     -- ASSIGN  |PARTITIONED|
-      project ([$$111]) [cardinality: 1003.05, op-cost: 0.0, total-cost: 34411.56]
+      project ([$$111]) [cardinality: 1003.97, op-cost: 0.0, total-cost: 34439.83]
       -- STREAM_PROJECT  |PARTITIONED|
-        exchange [cardinality: 1003.05, op-cost: 0.0, total-cost: 34411.56]
+        exchange [cardinality: 1003.97, op-cost: 0.0, total-cost: 34439.83]
         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
           group by ([$$l_linenumber := $$115; $$o_orderstatus := $$116; $$c_nationkey := $$117]) decor ([]) {
                     aggregate [$$111] <- [sql-sum-serial($$114)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                     -- AGGREGATE  |LOCAL|
                       nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                       -- NESTED_TUPLE_SOURCE  |LOCAL|
-                 } [cardinality: 1003.05, op-cost: 6018.17, total-cost: 34411.56]
+                 } [cardinality: 1003.97, op-cost: 6023.83, total-cost: 34439.83]
           -- EXTERNAL_GROUP_BY[$$115, $$116, $$117]  |PARTITIONED|
-            exchange [cardinality: 1003.05, op-cost: 0.0, total-cost: 28393.39]
+            exchange [cardinality: 1003.97, op-cost: 0.0, total-cost: 28416.0]
             -- HASH_PARTITION_EXCHANGE [$$115, $$116, $$117]  |PARTITIONED|
               group by ([$$115 := $$106; $$116 := $$103; $$117 := $$104]) decor ([]) {
                         aggregate [$$114] <- [sql-count-serial(1)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                         -- AGGREGATE  |LOCAL|
                           nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                           -- NESTED_TUPLE_SOURCE  |LOCAL|
-                     } [cardinality: 1003.05, op-cost: 6018.17, total-cost: 28393.39]
+                     } [cardinality: 1003.97, op-cost: 6023.83, total-cost: 28416.0]
               -- EXTERNAL_GROUP_BY[$$106, $$103, $$104]  |PARTITIONED|
-                exchange [cardinality: 6018.17, op-cost: 0.0, total-cost: 22375.22]
+                exchange [cardinality: 6023.83, op-cost: 0.0, total-cost: 22392.17]
                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                  project ([$$106, $$103, $$104]) [cardinality: 6018.17, op-cost: 0.0, total-cost: 22375.22]
+                  project ([$$106, $$103, $$104]) [cardinality: 6023.83, op-cost: 0.0, total-cost: 22392.17]
                   -- STREAM_PROJECT  |PARTITIONED|
-                    exchange [cardinality: 6018.17, op-cost: 0.0, total-cost: 22375.22]
+                    exchange [cardinality: 6023.83, op-cost: 0.0, total-cost: 22392.17]
                     -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                      unnest-map [$$105, $$106, $$l] <- index-search("LineItem", 0, "Default", "tpch", "LineItem", true, true, 1, $$107, 1, $$107, true, true, true) [cardinality: 6018.17, op-cost: 12018.16, total-cost: 22375.22]
+                      unnest-map [$$105, $$106, $$l] <- index-search("LineItem", 0, "Default", "tpch", "LineItem", true, true, 1, $$107, 1, $$107, true, true, true) [cardinality: 6023.83, op-cost: 12029.47, total-cost: 22392.17]
                       -- BTREE_SEARCH  |PARTITIONED|
                         exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                         -- BROADCAST_EXCHANGE  |PARTITIONED|
@@ -38,7 +38,7 @@
                           -- STREAM_PROJECT  |PARTITIONED|
                             exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                              join (eq($$109, $$108)) [cardinality: 1501.41, op-cost: 2101.41, total-cost: 4351.41]
+                              join (eq($$109, $$108)) [cardinality: 1502.82, op-cost: 2101.41, total-cost: 4351.41]
                               -- HYBRID_HASH_JOIN [$$109][$$108]  |PARTITIONED|
                                 exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.7.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.7.plan
index 9e4eff7..66a35b7 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.7.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.7.plan
@@ -1,36 +1,36 @@
-distribute result [$$101] [cardinality: 1003.05, op-cost: 0.0, total-cost: 34411.56]
+distribute result [$$101] [cardinality: 1003.97, op-cost: 0.0, total-cost: 34439.83]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 1003.05, op-cost: 0.0, total-cost: 34411.56]
+  exchange [cardinality: 1003.97, op-cost: 0.0, total-cost: 34439.83]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$101] <- [{"$1": $$111}] project: [$$101] [cardinality: 1003.05, op-cost: 0.0, total-cost: 34411.56]
+    assign [$$101] <- [{"$1": $$111}] project: [$$101] [cardinality: 1003.97, op-cost: 0.0, total-cost: 34439.83]
     -- ASSIGN  |PARTITIONED|
-      project ([$$111]) [cardinality: 1003.05, op-cost: 0.0, total-cost: 34411.56]
+      project ([$$111]) [cardinality: 1003.97, op-cost: 0.0, total-cost: 34439.83]
       -- STREAM_PROJECT  |PARTITIONED|
-        exchange [cardinality: 1003.05, op-cost: 0.0, total-cost: 34411.56]
+        exchange [cardinality: 1003.97, op-cost: 0.0, total-cost: 34439.83]
         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
           group by ([$$l_linenumber := $$115; $$c_nationkey := $$116; $$o_orderstatus := $$117]) decor ([]) {
                     aggregate [$$111] <- [sql-sum-serial($$114)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                     -- AGGREGATE  |LOCAL|
                       nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                       -- NESTED_TUPLE_SOURCE  |LOCAL|
-                 } [cardinality: 1003.05, op-cost: 6018.17, total-cost: 34411.56]
+                 } [cardinality: 1003.97, op-cost: 6023.83, total-cost: 34439.83]
           -- EXTERNAL_GROUP_BY[$$115, $$116, $$117]  |PARTITIONED|
-            exchange [cardinality: 1003.05, op-cost: 0.0, total-cost: 28393.39]
+            exchange [cardinality: 1003.97, op-cost: 0.0, total-cost: 28416.0]
             -- HASH_PARTITION_EXCHANGE [$$115, $$116, $$117]  |PARTITIONED|
               group by ([$$115 := $$106; $$116 := $$103; $$117 := $$104]) decor ([]) {
                         aggregate [$$114] <- [sql-count-serial(1)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                         -- AGGREGATE  |LOCAL|
                           nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                           -- NESTED_TUPLE_SOURCE  |LOCAL|
-                     } [cardinality: 1003.05, op-cost: 6018.17, total-cost: 28393.39]
+                     } [cardinality: 1003.97, op-cost: 6023.83, total-cost: 28416.0]
               -- EXTERNAL_GROUP_BY[$$106, $$103, $$104]  |PARTITIONED|
-                exchange [cardinality: 6018.17, op-cost: 0.0, total-cost: 22375.22]
+                exchange [cardinality: 6023.83, op-cost: 0.0, total-cost: 22392.17]
                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                  project ([$$106, $$103, $$104]) [cardinality: 6018.17, op-cost: 0.0, total-cost: 22375.22]
+                  project ([$$106, $$103, $$104]) [cardinality: 6023.83, op-cost: 0.0, total-cost: 22392.17]
                   -- STREAM_PROJECT  |PARTITIONED|
-                    exchange [cardinality: 6018.17, op-cost: 0.0, total-cost: 22375.22]
+                    exchange [cardinality: 6023.83, op-cost: 0.0, total-cost: 22392.17]
                     -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                      unnest-map [$$105, $$106, $$l] <- index-search("LineItem", 0, "Default", "tpch", "LineItem", true, true, 1, $$107, 1, $$107, true, true, true) [cardinality: 6018.17, op-cost: 12018.16, total-cost: 22375.22]
+                      unnest-map [$$105, $$106, $$l] <- index-search("LineItem", 0, "Default", "tpch", "LineItem", true, true, 1, $$107, 1, $$107, true, true, true) [cardinality: 6023.83, op-cost: 12029.47, total-cost: 22392.17]
                       -- BTREE_SEARCH  |PARTITIONED|
                         exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                         -- BROADCAST_EXCHANGE  |PARTITIONED|
@@ -38,7 +38,7 @@
                           -- STREAM_PROJECT  |PARTITIONED|
                             exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                              join (eq($$109, $$108)) [cardinality: 1501.41, op-cost: 2101.41, total-cost: 4351.41]
+                              join (eq($$109, $$108)) [cardinality: 1502.82, op-cost: 2101.41, total-cost: 4351.41]
                               -- HYBRID_HASH_JOIN [$$109][$$108]  |PARTITIONED|
                                 exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.8.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.8.plan
index baa6c88..17e8018 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.8.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/cardinality-estimation/join-queries/join-queries.8.plan
@@ -1,34 +1,34 @@
-distribute result [$$119] [cardinality: 25.0, op-cost: 0.0, total-cost: 7109.8]
+distribute result [$$119] [cardinality: 25.0, op-cost: 0.0, total-cost: 6972.1]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 7109.8]
+  exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 6972.1]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$119] <- [{"n_name": $$n_name, "revenue": $$132}] project: [$$119] [cardinality: 25.0, op-cost: 0.0, total-cost: 7109.8]
+    assign [$$119] <- [{"n_name": $$n_name, "revenue": $$132}] project: [$$119] [cardinality: 25.0, op-cost: 0.0, total-cost: 6972.1]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 7109.8]
+      exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 6972.1]
       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
         group by ([$$n_name := $$142]) decor ([]) {
                   aggregate [$$132] <- [global-sql-sum-serial($$141)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                   -- AGGREGATE  |LOCAL|
                     nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                     -- NESTED_TUPLE_SOURCE  |LOCAL|
-               } [cardinality: 25.0, op-cost: 110.61, total-cost: 7109.8]
+               } [cardinality: 25.0, op-cost: 39.89, total-cost: 6972.1]
         -- EXTERNAL_GROUP_BY[$$142]  |PARTITIONED|
-          exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 6999.19]
+          exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 6932.21]
           -- HASH_PARTITION_EXCHANGE [$$142]  |PARTITIONED|
             group by ([$$142 := $$120]) decor ([]) {
                       aggregate [$$141] <- [local-sql-sum-serial(numeric-multiply($$139, numeric-subtract(1, $$140)))] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                       -- AGGREGATE  |LOCAL|
                         nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                         -- NESTED_TUPLE_SOURCE  |LOCAL|
-                   } [cardinality: 25.0, op-cost: 110.61, total-cost: 6999.19]
+                   } [cardinality: 25.0, op-cost: 39.89, total-cost: 6932.21]
             -- EXTERNAL_GROUP_BY[$$120]  |PARTITIONED|
-              exchange [cardinality: 110.61, op-cost: 0.0, total-cost: 6888.58]
+              exchange [cardinality: 39.89, op-cost: 0.0, total-cost: 6892.32]
               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                project ([$$139, $$140, $$120]) [cardinality: 110.61, op-cost: 0.0, total-cost: 6888.58]
+                project ([$$139, $$140, $$120]) [cardinality: 39.89, op-cost: 0.0, total-cost: 6892.32]
                 -- STREAM_PROJECT  |PARTITIONED|
-                  exchange [cardinality: 110.61, op-cost: 0.0, total-cost: 6888.58]
+                  exchange [cardinality: 39.89, op-cost: 0.0, total-cost: 6892.32]
                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                    join (and(eq($$130, $$128), eq($$133, $$127))) [cardinality: 110.61, op-cost: 1035.49, total-cost: 6888.58]
+                    join (and(eq($$130, $$128), eq($$133, $$127))) [cardinality: 39.89, op-cost: 1036.42, total-cost: 6892.32]
                     -- HYBRID_HASH_JOIN [$$128, $$133][$$130, $$127]  |PARTITIONED|
                       exchange [cardinality: 6010.65, op-cost: 0.0, total-cost: 6005.0]
                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
@@ -46,7 +46,7 @@
                                   -- STREAM_PROJECT  |PARTITIONED|
                                     exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                     -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                      join (eq($$123, $$136)) [cardinality: 248.35, op-cost: 398.35, total-cost: 2821.71]
+                                      join (eq($$123, $$136)) [cardinality: 248.59, op-cost: 398.35, total-cost: 2821.71]
                                       -- HYBRID_HASH_JOIN [$$123][$$136]  |PARTITIONED|
                                         exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                         -- HASH_PARTITION_EXCHANGE [$$123]  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/pushdown/field-access-pushdown/field-access-pushdown.008.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/pushdown/field-access-pushdown/field-access-pushdown.008.plan
index f7797b0..373f85d 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/pushdown/field-access-pushdown/field-access-pushdown.008.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/pushdown/field-access-pushdown/field-access-pushdown.008.plan
@@ -1,22 +1,22 @@
-distribute result [$$31] [cardinality: 8.0, op-cost: 0.0, total-cost: 69.0]
+distribute result [$$31] [cardinality: 2.1, op-cost: 0.0, total-cost: 47.25]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 8.0, op-cost: 0.0, total-cost: 69.0]
+  exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 47.25]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$31] <- [{"age": $$38, "name": $$39}] project: [$$31] [cardinality: 8.0, op-cost: 0.0, total-cost: 69.0]
+    assign [$$31] <- [{"age": $$38, "name": $$39}] project: [$$31] [cardinality: 2.1, op-cost: 0.0, total-cost: 47.25]
     -- ASSIGN  |PARTITIONED|
-      project ([$$38, $$39]) [cardinality: 8.0, op-cost: 0.0, total-cost: 69.0]
+      project ([$$38, $$39]) [cardinality: 2.1, op-cost: 0.0, total-cost: 47.25]
       -- STREAM_PROJECT  |PARTITIONED|
-        exchange [cardinality: 8.0, op-cost: 0.0, total-cost: 69.0]
+        exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 47.25]
         -- SORT_MERGE_EXCHANGE [$$34(ASC) ]  |PARTITIONED|
-          order (ASC, $$34) [cardinality: 8.0, op-cost: 24.0, total-cost: 69.0]
+          order (ASC, $$34) [cardinality: 2.1, op-cost: 2.25, total-cost: 47.25]
           -- STABLE_SORT [$$34(ASC)]  |PARTITIONED|
-            exchange [cardinality: 8.0, op-cost: 0.0, total-cost: 45.0]
+            exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 45.0]
             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-              project ([$$38, $$39, $$34]) [cardinality: 8.0, op-cost: 0.0, total-cost: 45.0]
+              project ([$$38, $$39, $$34]) [cardinality: 2.1, op-cost: 0.0, total-cost: 45.0]
               -- STREAM_PROJECT  |PARTITIONED|
-                exchange [cardinality: 8.0, op-cost: 0.0, total-cost: 45.0]
+                exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 45.0]
                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                  join (eq($$33, $$34)) [cardinality: 8.0, op-cost: 15.0, total-cost: 45.0]
+                  join (eq($$33, $$34)) [cardinality: 2.1, op-cost: 15.0, total-cost: 45.0]
                   -- HYBRID_HASH_JOIN [$$34][$$33]  |PARTITIONED|
                     exchange [cardinality: 8.0, op-cost: 8.0, total-cost: 16.0]
                     -- HASH_PARTITION_EXCHANGE [$$34]  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.04.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.04.plan
index b5e1560..d72bdc5 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.04.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.04.plan
@@ -1,58 +1,60 @@
-distribute result [$$51] [cardinality: 166.67, op-cost: 0.0, total-cost: 1840.73]
+distribute result [$$51] [cardinality: 58.0, op-cost: 0.0, total-cost: 819.76]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 1840.73]
+  exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 819.76]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$51] <- [{"n_nationkey": $$58, "s_nationkey": $$56, "c_nationkey": $$55}] project: [$$51] [cardinality: 166.67, op-cost: 0.0, total-cost: 1840.73]
+    assign [$$51] <- [{"n_nationkey": $$58, "s_nationkey": $$56, "c_nationkey": $$55}] project: [$$51] [cardinality: 58.0, op-cost: 0.0, total-cost: 819.76]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 1840.73]
+      exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 819.76]
       -- SORT_MERGE_EXCHANGE [$$58(ASC), $$56(ASC), $$55(ASC) ]  |PARTITIONED|
-        order (ASC, $$58) (ASC, $$56) (ASC, $$55) [cardinality: 166.67, op-cost: 1230.17, total-cost: 1840.73]
+        order (ASC, $$58) (ASC, $$56) (ASC, $$55) [cardinality: 58.0, op-cost: 339.76, total-cost: 819.76]
         -- STABLE_SORT [$$58(ASC), $$56(ASC), $$55(ASC)]  |PARTITIONED|
-          exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 610.56]
+          exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 480.0]
           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-            join (eq($$55, $$58)) [cardinality: 166.67, op-cost: 177.78, total-cost: 610.56]
-            -- HYBRID_HASH_JOIN [$$55][$$58]  |PARTITIONED|
-              exchange [cardinality: 150.0, op-cost: 150.0, total-cost: 300.0]
-              -- HASH_PARTITION_EXCHANGE [$$55]  |PARTITIONED|
-                assign [$$55] <- [$$c.getField(3)] project: [$$55] [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
-                -- ASSIGN  |PARTITIONED|
-                  project ([$$c]) [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
-                  -- STREAM_PROJECT  |PARTITIONED|
-                    exchange [cardinality: 150.0, op-cost: 150.0, total-cost: 300.0]
-                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                      data-scan []<-[$$60, $$c] <- tpch.Customer [cardinality: 150.0, op-cost: 150.0, total-cost: 150.0]
-                      -- DATASOURCE_SCAN  |PARTITIONED|
-                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                          empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-              exchange [cardinality: 27.78, op-cost: 27.78, total-cost: 132.78]
+            project ([$$58, $$56, $$55]) [cardinality: 58.0, op-cost: 0.0, total-cost: 480.0]
+            -- STREAM_PROJECT  |PARTITIONED|
+              exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 480.0]
               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                join (eq($$56, $$58)) [cardinality: 27.78, op-cost: 35.0, total-cost: 105.0]
-                -- HYBRID_HASH_JOIN [$$58][$$56]  |PARTITIONED|
-                  exchange [cardinality: 25.0, op-cost: 25.0, total-cost: 50.0]
-                  -- HASH_PARTITION_EXCHANGE [$$58]  |PARTITIONED|
-                    project ([$$58]) [cardinality: 25.0, op-cost: 0.0, total-cost: 25.0]
+                join (and(eq($$55, $$58), eq($$56, $$66))) [cardinality: 58.0, op-cost: 190.0, total-cost: 480.0]
+                -- HYBRID_HASH_JOIN [$$55, $$66][$$58, $$56]  |PARTITIONED|
+                  exchange [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                    assign [$$66] <- [$$55] [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
+                    -- ASSIGN  |PARTITIONED|
+                      assign [$$55] <- [$$c.getField(3)] project: [$$55] [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
+                      -- ASSIGN  |PARTITIONED|
+                        project ([$$c]) [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
+                        -- STREAM_PROJECT  |PARTITIONED|
+                          exchange [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            data-scan []<-[$$60, $$c] <- tpch.Customer [cardinality: 150.0, op-cost: 150.0, total-cost: 150.0]
+                            -- DATASOURCE_SCAN  |PARTITIONED|
+                              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                  exchange [cardinality: 10.0, op-cost: 40.0, total-cost: 140.0]
+                  -- BROADCAST_EXCHANGE  |PARTITIONED|
+                    project ([$$56, $$58]) [cardinality: 10.0, op-cost: 0.0, total-cost: 100.0]
                     -- STREAM_PROJECT  |PARTITIONED|
-                      exchange [cardinality: 25.0, op-cost: 25.0, total-cost: 50.0]
+                      exchange [cardinality: 10.0, op-cost: 40.0, total-cost: 140.0]
                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                        data-scan []<-[$$58, $$n] <- tpch.Nation [cardinality: 25.0, op-cost: 25.0, total-cost: 25.0]
-                        -- DATASOURCE_SCAN  |PARTITIONED|
+                        unnest-map [$$58, $$n] <- index-search("Nation", 0, "Default", "tpch", "Nation", true, true, 1, $$56, 1, $$56, true, true, true) [cardinality: 10.0, op-cost: 50.0, total-cost: 100.0]
+                        -- BTREE_SEARCH  |PARTITIONED|
                           exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                            empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                  exchange [cardinality: 10.0, op-cost: 10.0, total-cost: 20.0]
-                  -- HASH_PARTITION_EXCHANGE [$$56]  |PARTITIONED|
-                    assign [$$56] <- [$$s.getField(3)] project: [$$56] [cardinality: 10.0, op-cost: 0.0, total-cost: 10.0]
-                    -- ASSIGN  |PARTITIONED|
-                      project ([$$s]) [cardinality: 10.0, op-cost: 0.0, total-cost: 10.0]
-                      -- STREAM_PROJECT  |PARTITIONED|
-                        exchange [cardinality: 10.0, op-cost: 10.0, total-cost: 20.0]
-                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                          data-scan []<-[$$59, $$s] <- tpch.Supplier [cardinality: 10.0, op-cost: 10.0, total-cost: 10.0]
-                          -- DATASOURCE_SCAN  |PARTITIONED|
-                            exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                              empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-                              -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                            order (ASC, $$56) [cardinality: 10.0, op-cost: 50.0, total-cost: 100.0]
+                            -- STABLE_SORT [$$56(ASC)]  |PARTITIONED|
+                              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                              -- HASH_PARTITION_EXCHANGE [$$56]  |PARTITIONED|
+                                assign [$$56] <- [$$s.getField(3)] project: [$$56] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                -- ASSIGN  |PARTITIONED|
+                                  project ([$$s]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      data-scan []<-[$$59, $$s] <- tpch.Supplier [cardinality: 10.0, op-cost: 10.0, total-cost: 10.0]
+                                      -- DATASOURCE_SCAN  |PARTITIONED|
+                                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.10.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.10.plan
index 1bc2627..fd115a7 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.10.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.10.plan
@@ -1,58 +1,60 @@
-distribute result [$$51] [cardinality: 166.67, op-cost: 0.0, total-cost: 1801.84]
+distribute result [$$51] [cardinality: 58.0, op-cost: 0.0, total-cost: 789.76]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 1801.84]
+  exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 789.76]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$51] <- [{"n_nationkey": $$58, "s_nationkey": $$56, "c_nationkey": $$55}] project: [$$51] [cardinality: 166.67, op-cost: 0.0, total-cost: 1801.84]
+    assign [$$51] <- [{"n_nationkey": $$58, "s_nationkey": $$56, "c_nationkey": $$55}] project: [$$51] [cardinality: 58.0, op-cost: 0.0, total-cost: 789.76]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 1801.84]
+      exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 789.76]
       -- SORT_MERGE_EXCHANGE [$$58(ASC), $$56(ASC), $$55(ASC) ]  |PARTITIONED|
-        order (ASC, $$58) (ASC, $$56) (ASC, $$55) [cardinality: 166.67, op-cost: 1230.17, total-cost: 1801.84]
+        order (ASC, $$58) (ASC, $$56) (ASC, $$55) [cardinality: 58.0, op-cost: 339.76, total-cost: 789.76]
         -- STABLE_SORT [$$58(ASC), $$56(ASC), $$55(ASC)]  |PARTITIONED|
-          exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 571.67]
+          exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 450.0]
           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-            join (eq($$55, $$58)) [cardinality: 166.67, op-cost: 233.33, total-cost: 571.67]
-            -- HYBRID_HASH_JOIN [$$55][$$58]  |PARTITIONED|
-              exchange [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
-              -- RANDOM_PARTITION_EXCHANGE  |PARTITIONED|
-                assign [$$55] <- [$$c.getField(3)] project: [$$55] [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
-                -- ASSIGN  |PARTITIONED|
-                  project ([$$c]) [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
-                  -- STREAM_PROJECT  |PARTITIONED|
-                    exchange [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
-                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                      data-scan []<-[$$60, $$c] <- tpch.Customer [cardinality: 150.0, op-cost: 150.0, total-cost: 150.0]
-                      -- DATASOURCE_SCAN  |PARTITIONED|
-                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                          empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-              exchange [cardinality: 27.78, op-cost: 83.33, total-cost: 188.33]
-              -- BROADCAST_EXCHANGE  |PARTITIONED|
-                join (eq($$56, $$58)) [cardinality: 27.78, op-cost: 35.0, total-cost: 105.0]
-                -- HYBRID_HASH_JOIN [$$58][$$56]  |PARTITIONED|
-                  exchange [cardinality: 25.0, op-cost: 25.0, total-cost: 50.0]
-                  -- HASH_PARTITION_EXCHANGE [$$58]  |PARTITIONED|
-                    project ([$$58]) [cardinality: 25.0, op-cost: 0.0, total-cost: 25.0]
+            project ([$$58, $$56, $$55]) [cardinality: 58.0, op-cost: 0.0, total-cost: 450.0]
+            -- STREAM_PROJECT  |PARTITIONED|
+              exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 450.0]
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                join (and(eq($$55, $$58), eq($$56, $$66))) [cardinality: 58.0, op-cost: 180.0, total-cost: 450.0]
+                -- HYBRID_HASH_JOIN [$$55, $$66][$$58, $$56]  |PARTITIONED|
+                  exchange [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
+                  -- RANDOM_PARTITION_EXCHANGE  |PARTITIONED|
+                    assign [$$66] <- [$$55] [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
+                    -- ASSIGN  |PARTITIONED|
+                      assign [$$55] <- [$$c.getField(3)] project: [$$55] [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
+                      -- ASSIGN  |PARTITIONED|
+                        project ([$$c]) [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
+                        -- STREAM_PROJECT  |PARTITIONED|
+                          exchange [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            data-scan []<-[$$60, $$c] <- tpch.Customer [cardinality: 150.0, op-cost: 150.0, total-cost: 150.0]
+                            -- DATASOURCE_SCAN  |PARTITIONED|
+                              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                  exchange [cardinality: 10.0, op-cost: 30.0, total-cost: 120.0]
+                  -- BROADCAST_EXCHANGE  |PARTITIONED|
+                    project ([$$56, $$58]) [cardinality: 10.0, op-cost: 0.0, total-cost: 90.0]
                     -- STREAM_PROJECT  |PARTITIONED|
-                      exchange [cardinality: 25.0, op-cost: 25.0, total-cost: 50.0]
+                      exchange [cardinality: 10.0, op-cost: 30.0, total-cost: 120.0]
                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                        data-scan []<-[$$58, $$n] <- tpch.Nation [cardinality: 25.0, op-cost: 25.0, total-cost: 25.0]
-                        -- DATASOURCE_SCAN  |PARTITIONED|
+                        unnest-map [$$58, $$n] <- index-search("Nation", 0, "Default", "tpch", "Nation", true, true, 1, $$56, 1, $$56, true, true, true) [cardinality: 10.0, op-cost: 50.0, total-cost: 90.0]
+                        -- BTREE_SEARCH  |PARTITIONED|
                           exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                            empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                  exchange [cardinality: 10.0, op-cost: 10.0, total-cost: 20.0]
-                  -- HASH_PARTITION_EXCHANGE [$$56]  |PARTITIONED|
-                    assign [$$56] <- [$$s.getField(3)] project: [$$56] [cardinality: 10.0, op-cost: 0.0, total-cost: 10.0]
-                    -- ASSIGN  |PARTITIONED|
-                      project ([$$s]) [cardinality: 10.0, op-cost: 0.0, total-cost: 10.0]
-                      -- STREAM_PROJECT  |PARTITIONED|
-                        exchange [cardinality: 10.0, op-cost: 10.0, total-cost: 20.0]
-                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                          data-scan []<-[$$59, $$s] <- tpch.Supplier [cardinality: 10.0, op-cost: 10.0, total-cost: 10.0]
-                          -- DATASOURCE_SCAN  |PARTITIONED|
-                            exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                              empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-                              -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                            order (ASC, $$56) [cardinality: 10.0, op-cost: 50.0, total-cost: 90.0]
+                            -- STABLE_SORT [$$56(ASC)]  |PARTITIONED|
+                              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                              -- HASH_PARTITION_EXCHANGE [$$56]  |PARTITIONED|
+                                assign [$$56] <- [$$s.getField(3)] project: [$$56] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                -- ASSIGN  |PARTITIONED|
+                                  project ([$$s]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      data-scan []<-[$$59, $$s] <- tpch.Supplier [cardinality: 10.0, op-cost: 10.0, total-cost: 10.0]
+                                      -- DATASOURCE_SCAN  |PARTITIONED|
+                                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.12.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.12.plan
index d1d77bd..b1ac963 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.12.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.12.plan
@@ -1,20 +1,20 @@
-distribute result [$$51] [cardinality: 166.67, op-cost: 0.0, total-cost: 1840.73]
+distribute result [$$51] [cardinality: 58.0, op-cost: 0.0, total-cost: 914.76]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 1840.73]
+  exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 914.76]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$51] <- [{"n_nationkey": $$58, "s_nationkey": $$56, "c_nationkey": $$55}] project: [$$51] [cardinality: 166.67, op-cost: 0.0, total-cost: 1840.73]
+    assign [$$51] <- [{"n_nationkey": $$58, "s_nationkey": $$56, "c_nationkey": $$55}] project: [$$51] [cardinality: 58.0, op-cost: 0.0, total-cost: 914.76]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 1840.73]
+      exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 914.76]
       -- SORT_MERGE_EXCHANGE [$$58(ASC), $$56(ASC), $$55(ASC) ]  |PARTITIONED|
-        order (ASC, $$58) (ASC, $$56) (ASC, $$55) [cardinality: 166.67, op-cost: 1230.17, total-cost: 1840.73]
+        order (ASC, $$58) (ASC, $$56) (ASC, $$55) [cardinality: 58.0, op-cost: 339.76, total-cost: 914.76]
         -- STABLE_SORT [$$58(ASC), $$56(ASC), $$55(ASC)]  |PARTITIONED|
-          exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 610.56]
+          exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 575.0]
           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-            join (eq($$55, $$58)) [cardinality: 166.67, op-cost: 177.78, total-cost: 610.56]
+            join (eq($$55, $$58)) [cardinality: 58.0, op-cost: 160.0, total-cost: 575.0]
             -- HYBRID_HASH_JOIN [$$58][$$55]  |PARTITIONED|
-              exchange [cardinality: 27.78, op-cost: 27.78, total-cost: 132.78]
+              exchange [cardinality: 10.0, op-cost: 10.0, total-cost: 115.0]
               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                join (eq($$56, $$58)) [cardinality: 27.78, op-cost: 35.0, total-cost: 105.0]
+                join (eq($$56, $$58)) [cardinality: 10.0, op-cost: 35.0, total-cost: 105.0]
                 -- HYBRID_HASH_JOIN [$$58][$$56]  |PARTITIONED|
                   exchange [cardinality: 25.0, op-cost: 25.0, total-cost: 50.0]
                   -- HASH_PARTITION_EXCHANGE [$$58]  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.14.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.14.plan
index aef155b..1b1c926 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.14.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.14.plan
@@ -1,30 +1,30 @@
-distribute result [$$51] [cardinality: 166.67, op-cost: 0.0, total-cost: 1853.5]
+distribute result [$$51] [cardinality: 58.0, op-cost: 0.0, total-cost: 819.76]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 1853.5]
+  exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 819.76]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$51] <- [{"n_nationkey": $$59, "s_nationkey": $$56, "c_nationkey": $$55}] project: [$$51] [cardinality: 166.67, op-cost: 0.0, total-cost: 1853.5]
+    assign [$$51] <- [{"n_nationkey": $$59, "s_nationkey": $$56, "c_nationkey": $$55}] project: [$$51] [cardinality: 58.0, op-cost: 0.0, total-cost: 819.76]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 1853.5]
+      exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 819.76]
       -- SORT_MERGE_EXCHANGE [$$59(ASC), $$56(ASC), $$55(ASC) ]  |PARTITIONED|
-        order (ASC, $$59) (ASC, $$56) (ASC, $$55) [cardinality: 166.67, op-cost: 1230.17, total-cost: 1853.5]
+        order (ASC, $$59) (ASC, $$56) (ASC, $$55) [cardinality: 58.0, op-cost: 339.76, total-cost: 819.76]
         -- STABLE_SORT [$$59(ASC), $$56(ASC), $$55(ASC)]  |PARTITIONED|
-          exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 623.33]
+          exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 480.0]
           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-            project ([$$59, $$56, $$55]) [cardinality: 166.67, op-cost: 0.0, total-cost: 623.33]
+            project ([$$59, $$56, $$55]) [cardinality: 58.0, op-cost: 0.0, total-cost: 480.0]
             -- STREAM_PROJECT  |PARTITIONED|
-              exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 623.33]
+              exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 480.0]
               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                join (and(eq($$55, $$59), eq($$56, $$66))) [cardinality: 166.67, op-cost: 177.78, total-cost: 623.33]
+                join (and(eq($$55, $$59), eq($$56, $$66))) [cardinality: 58.0, op-cost: 190.0, total-cost: 480.0]
                 -- HYBRID_HASH_JOIN [$$55, $$66][$$59, $$56]  |PARTITIONED|
-                  exchange [cardinality: 150.0, op-cost: 150.0, total-cost: 300.0]
-                  -- HASH_PARTITION_EXCHANGE [$$55, $$66]  |PARTITIONED|
+                  exchange [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                     assign [$$66] <- [$$55] [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
                     -- ASSIGN  |PARTITIONED|
                       assign [$$55] <- [$$c.getField(3)] project: [$$55] [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
                       -- ASSIGN  |PARTITIONED|
                         project ([$$c]) [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
                         -- STREAM_PROJECT  |PARTITIONED|
-                          exchange [cardinality: 150.0, op-cost: 150.0, total-cost: 300.0]
+                          exchange [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                             data-scan []<-[$$60, $$c] <- tpch.Customer [cardinality: 150.0, op-cost: 150.0, total-cost: 150.0]
                             -- DATASOURCE_SCAN  |PARTITIONED|
@@ -32,17 +32,17 @@
                               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                 empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                 -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                  exchange [cardinality: 27.78, op-cost: 27.78, total-cost: 145.56]
-                  -- HASH_PARTITION_EXCHANGE [$$59, $$56]  |PARTITIONED|
-                    project ([$$56, $$59]) [cardinality: 27.78, op-cost: 0.0, total-cost: 117.78]
+                  exchange [cardinality: 10.0, op-cost: 40.0, total-cost: 140.0]
+                  -- BROADCAST_EXCHANGE  |PARTITIONED|
+                    project ([$$56, $$59]) [cardinality: 10.0, op-cost: 0.0, total-cost: 100.0]
                     -- STREAM_PROJECT  |PARTITIONED|
-                      exchange [cardinality: 27.78, op-cost: 27.78, total-cost: 145.56]
+                      exchange [cardinality: 10.0, op-cost: 40.0, total-cost: 140.0]
                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                        unnest-map [$$59, $$n] <- index-search("Nation", 0, "Default", "tpch", "Nation", true, true, 1, $$56, 1, $$56, true, true, true) [cardinality: 27.78, op-cost: 67.78, total-cost: 117.78]
+                        unnest-map [$$59, $$n] <- index-search("Nation", 0, "Default", "tpch", "Nation", true, true, 1, $$56, 1, $$56, true, true, true) [cardinality: 10.0, op-cost: 50.0, total-cost: 100.0]
                         -- BTREE_SEARCH  |PARTITIONED|
                           exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                            order (ASC, $$56) [cardinality: 27.78, op-cost: 67.78, total-cost: 117.78]
+                            order (ASC, $$56) [cardinality: 10.0, op-cost: 50.0, total-cost: 100.0]
                             -- STABLE_SORT [$$56(ASC)]  |PARTITIONED|
                               exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                               -- HASH_PARTITION_EXCHANGE [$$56]  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.16.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.16.plan
index 8a4aa88..37af1af 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.16.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/join/hash-join-with-redundant-variable/hash-join-with-redundant-variable.16.plan
@@ -1,20 +1,20 @@
-distribute result [$$51] [cardinality: 166.67, op-cost: 0.0, total-cost: 1804.61]
+distribute result [$$51] [cardinality: 58.0, op-cost: 0.0, total-cost: 789.76]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 1804.61]
+  exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 789.76]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$51] <- [{"n_nationkey": $$59, "s_nationkey": $$56, "c_nationkey": $$55}] project: [$$51] [cardinality: 166.67, op-cost: 0.0, total-cost: 1804.61]
+    assign [$$51] <- [{"n_nationkey": $$59, "s_nationkey": $$56, "c_nationkey": $$55}] project: [$$51] [cardinality: 58.0, op-cost: 0.0, total-cost: 789.76]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 1804.61]
+      exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 789.76]
       -- SORT_MERGE_EXCHANGE [$$59(ASC), $$56(ASC), $$55(ASC) ]  |PARTITIONED|
-        order (ASC, $$59) (ASC, $$56) (ASC, $$55) [cardinality: 166.67, op-cost: 1230.17, total-cost: 1804.61]
+        order (ASC, $$59) (ASC, $$56) (ASC, $$55) [cardinality: 58.0, op-cost: 339.76, total-cost: 789.76]
         -- STABLE_SORT [$$59(ASC), $$56(ASC), $$55(ASC)]  |PARTITIONED|
-          exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 574.44]
+          exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 450.0]
           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-            project ([$$59, $$56, $$55]) [cardinality: 166.67, op-cost: 0.0, total-cost: 574.44]
+            project ([$$59, $$56, $$55]) [cardinality: 58.0, op-cost: 0.0, total-cost: 450.0]
             -- STREAM_PROJECT  |PARTITIONED|
-              exchange [cardinality: 166.67, op-cost: 0.0, total-cost: 574.44]
+              exchange [cardinality: 58.0, op-cost: 0.0, total-cost: 450.0]
               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                join (and(eq($$55, $$59), eq($$56, $$66))) [cardinality: 166.67, op-cost: 233.33, total-cost: 574.44]
+                join (and(eq($$55, $$59), eq($$56, $$66))) [cardinality: 58.0, op-cost: 180.0, total-cost: 450.0]
                 -- HYBRID_HASH_JOIN [$$55, $$66][$$59, $$56]  |PARTITIONED|
                   exchange [cardinality: 150.0, op-cost: 0.0, total-cost: 150.0]
                   -- RANDOM_PARTITION_EXCHANGE  |PARTITIONED|
@@ -32,17 +32,17 @@
                               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                 empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                 -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                  exchange [cardinality: 27.78, op-cost: 83.33, total-cost: 191.11]
+                  exchange [cardinality: 10.0, op-cost: 30.0, total-cost: 120.0]
                   -- BROADCAST_EXCHANGE  |PARTITIONED|
-                    project ([$$56, $$59]) [cardinality: 27.78, op-cost: 0.0, total-cost: 107.78]
+                    project ([$$56, $$59]) [cardinality: 10.0, op-cost: 0.0, total-cost: 90.0]
                     -- STREAM_PROJECT  |PARTITIONED|
-                      exchange [cardinality: 27.78, op-cost: 83.33, total-cost: 191.11]
+                      exchange [cardinality: 10.0, op-cost: 30.0, total-cost: 120.0]
                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                        unnest-map [$$59, $$n] <- index-search("Nation", 0, "Default", "tpch", "Nation", true, true, 1, $$56, 1, $$56, true, true, true) [cardinality: 27.78, op-cost: 67.78, total-cost: 107.78]
+                        unnest-map [$$59, $$n] <- index-search("Nation", 0, "Default", "tpch", "Nation", true, true, 1, $$56, 1, $$56, true, true, true) [cardinality: 10.0, op-cost: 50.0, total-cost: 90.0]
                         -- BTREE_SEARCH  |PARTITIONED|
                           exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                            order (ASC, $$56) [cardinality: 27.78, op-cost: 67.78, total-cost: 107.78]
+                            order (ASC, $$56) [cardinality: 10.0, op-cost: 50.0, total-cost: 90.0]
                             -- STABLE_SORT [$$56(ASC)]  |PARTITIONED|
                               exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                               -- HASH_PARTITION_EXCHANGE [$$56]  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.plan
index 160636d..b0c37fa 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.plan
@@ -1,16 +1,16 @@
-distribute result [$$52] [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+distribute result [$$52] [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+  exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$52] <- [{"t1_id": $$53, "t2_id": $$54}] project: [$$52] [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+    assign [$$52] <- [{"t1_id": $$53, "t2_id": $$54}] project: [$$52] [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+      exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
       -- SORT_MERGE_EXCHANGE [$$53(ASC), $$54(ASC) ]  |PARTITIONED|
-        order (ASC, $$53) (ASC, $$54) [cardinality: 6.0, op-cost: 15.51, total-cost: 39.51]
+        order (ASC, $$53) (ASC, $$54) [cardinality: 2.1, op-cost: 2.25, total-cost: 22.35]
         -- STABLE_SORT [$$53(ASC), $$54(ASC)]  |PARTITIONED|
-          exchange [cardinality: 6.0, op-cost: 0.0, total-cost: 24.0]
+          exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 20.1]
           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-            select ($$62) project: [$$53, $$54] [cardinality: 6.0, op-cost: 14.0, total-cost: 24.0]
+            select ($$62) project: [$$53, $$54] [cardinality: 2.1, op-cost: 10.1, total-cost: 20.1]
             -- STREAM_SELECT  |PARTITIONED|
               window-aggregate [$$62] <- [win-mark-first-missing-impl($$54)] partition [$$53] order (DESC, $$54) [cardinality: 2.0, op-cost: 0.0, total-cost: 2.0]
               -- WINDOW_STREAM  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
index 5efb1b8..6b12972 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
@@ -1,16 +1,16 @@
-distribute result [$$52] [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+distribute result [$$52] [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+  exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$52] <- [{"t1_id": $$73, "t2_id": $$54}] project: [$$52] [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+    assign [$$52] <- [{"t1_id": $$73, "t2_id": $$54}] project: [$$52] [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+      exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
       -- SORT_MERGE_EXCHANGE [$$73(ASC), $$54(ASC) ]  |PARTITIONED|
-        order (ASC, $$73) (ASC, $$54) [cardinality: 6.0, op-cost: 15.51, total-cost: 39.51]
+        order (ASC, $$73) (ASC, $$54) [cardinality: 2.1, op-cost: 2.25, total-cost: 22.35]
         -- STABLE_SORT [$$73(ASC), $$54(ASC)]  |PARTITIONED|
-          exchange [cardinality: 6.0, op-cost: 0.0, total-cost: 24.0]
+          exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 20.1]
           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-            select ($$74) project: [$$73, $$54] [cardinality: 6.0, op-cost: 14.0, total-cost: 24.0]
+            select ($$74) project: [$$73, $$54] [cardinality: 2.1, op-cost: 10.1, total-cost: 20.1]
             -- STREAM_SELECT  |PARTITIONED|
               window-aggregate [$$74] <- [win-mark-first-missing-impl($$54)] partition [$$73] order (DESC, $$54) [cardinality: 2.0, op-cost: 0.0, total-cost: 2.0]
               -- WINDOW_STREAM  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.plan
index 76995db..3bcf4ed 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.plan
@@ -1,16 +1,16 @@
-distribute result [$$52] [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+distribute result [$$52] [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+  exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$52] <- [{"t1_id": $$53, "t2_id": $$54}] project: [$$52] [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+    assign [$$52] <- [{"t1_id": $$53, "t2_id": $$54}] project: [$$52] [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+      exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
       -- SORT_MERGE_EXCHANGE [$$53(ASC), $$54(ASC) ]  |PARTITIONED|
-        order (ASC, $$53) (ASC, $$54) [cardinality: 6.0, op-cost: 15.51, total-cost: 39.51]
+        order (ASC, $$53) (ASC, $$54) [cardinality: 2.1, op-cost: 2.25, total-cost: 22.35]
         -- STABLE_SORT [$$53(ASC), $$54(ASC)]  |PARTITIONED|
-          exchange [cardinality: 6.0, op-cost: 0.0, total-cost: 24.0]
+          exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 20.1]
           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-            select ($$62) project: [$$53, $$54] [cardinality: 6.0, op-cost: 14.0, total-cost: 24.0]
+            select ($$62) project: [$$53, $$54] [cardinality: 2.1, op-cost: 10.1, total-cost: 20.1]
             -- STREAM_SELECT  |PARTITIONED|
               window-aggregate [$$62] <- [win-mark-first-missing-impl($$54)] partition [$$53] order (DESC, $$54) [cardinality: 2.0, op-cost: 0.0, total-cost: 2.0]
               -- WINDOW_STREAM  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
index 567ccef..6af4bd7 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
@@ -1,16 +1,16 @@
-distribute result [$$52] [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+distribute result [$$52] [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+  exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$52] <- [{"t1_id": $$73, "t2_id": $$54}] project: [$$52] [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+    assign [$$52] <- [{"t1_id": $$73, "t2_id": $$54}] project: [$$52] [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 6.0, op-cost: 0.0, total-cost: 39.51]
+      exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 22.35]
       -- SORT_MERGE_EXCHANGE [$$73(ASC), $$54(ASC) ]  |PARTITIONED|
-        order (ASC, $$73) (ASC, $$54) [cardinality: 6.0, op-cost: 15.51, total-cost: 39.51]
+        order (ASC, $$73) (ASC, $$54) [cardinality: 2.1, op-cost: 2.25, total-cost: 22.35]
         -- STABLE_SORT [$$73(ASC), $$54(ASC)]  |PARTITIONED|
-          exchange [cardinality: 6.0, op-cost: 0.0, total-cost: 24.0]
+          exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 20.1]
           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-            select ($$74) project: [$$73, $$54] [cardinality: 6.0, op-cost: 14.0, total-cost: 24.0]
+            select ($$74) project: [$$73, $$54] [cardinality: 2.1, op-cost: 10.1, total-cost: 20.1]
             -- STREAM_SELECT  |PARTITIONED|
               window-aggregate [$$74] <- [win-mark-first-missing-impl($$54)] partition [$$73] order (DESC, $$54) [cardinality: 2.0, op-cost: 0.0, total-cost: 2.0]
               -- WINDOW_STREAM  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/limit/push-limit-to-primary-scan-select/push-limit-to-primary-scan-select.5.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/limit/push-limit-to-primary-scan-select/push-limit-to-primary-scan-select.5.plan
index abe93b6..6b501a7 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/limit/push-limit-to-primary-scan-select/push-limit-to-primary-scan-select.5.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/limit/push-limit-to-primary-scan-select/push-limit-to-primary-scan-select.5.plan
@@ -1,20 +1,20 @@
-distribute result [$$37] [cardinality: 100.0, op-cost: 0.0, total-cost: 308.0]
+distribute result [$$37] [cardinality: 2.1, op-cost: 0.0, total-cost: 308.0]
 -- DISTRIBUTE_RESULT  |UNPARTITIONED|
-  exchange [cardinality: 100.0, op-cost: 0.0, total-cost: 308.0]
+  exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 308.0]
   -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
-    limit 2 [cardinality: 100.0, op-cost: 0.0, total-cost: 308.0]
+    limit 2 [cardinality: 2.1, op-cost: 0.0, total-cost: 308.0]
     -- STREAM_LIMIT  |UNPARTITIONED|
-      exchange [cardinality: 100.0, op-cost: 0.0, total-cost: 308.0]
+      exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 308.0]
       -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
-        assign [$$37] <- [{"dblpid": $$38}] project: [$$37] [cardinality: 100.0, op-cost: 0.0, total-cost: 308.0]
+        assign [$$37] <- [{"dblpid": $$38}] project: [$$37] [cardinality: 2.1, op-cost: 0.0, total-cost: 308.0]
         -- ASSIGN  |PARTITIONED|
-          limit 2 [cardinality: 100.0, op-cost: 0.0, total-cost: 308.0]
+          limit 2 [cardinality: 2.1, op-cost: 0.0, total-cost: 308.0]
           -- STREAM_LIMIT  |PARTITIONED|
-            project ([$$38]) [cardinality: 100.0, op-cost: 0.0, total-cost: 308.0]
+            project ([$$38]) [cardinality: 2.1, op-cost: 0.0, total-cost: 308.0]
             -- STREAM_PROJECT  |PARTITIONED|
-              exchange [cardinality: 100.0, op-cost: 0.0, total-cost: 308.0]
+              exchange [cardinality: 2.1, op-cost: 0.0, total-cost: 308.0]
               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                join (eq($$38, $$41)) [cardinality: 100.0, op-cost: 104.0, total-cost: 308.0]
+                join (eq($$38, $$41)) [cardinality: 2.1, op-cost: 104.0, total-cost: 308.0]
                 -- HYBRID_HASH_JOIN [$$38][$$41]  |PARTITIONED|
                   exchange [cardinality: 100.0, op-cost: 0.0, total-cost: 100.0]
                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.04.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.04.plan
index d2c7c52..1bb950f 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.04.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.04.plan
@@ -38,11 +38,11 @@
                                     -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                       join (and(eq($$250, $$203), eq($$202, $$231))) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                       -- HYBRID_HASH_JOIN [$$250, $$231][$$203, $$202]  |PARTITIONED|
-                                        exchange [cardinality: 30.16, op-cost: 0.0, total-cost: 264479.97]
+                                        exchange [cardinality: 29.05, op-cost: 0.0, total-cost: 264469.8]
                                         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                          assign [$$231] <- [get-item($$181, 0)] project: [$$250, $$244, $$231] [cardinality: 30.16, op-cost: 0.0, total-cost: 264479.97]
+                                          assign [$$231] <- [get-item($$181, 0)] project: [$$250, $$244, $$231] [cardinality: 29.05, op-cost: 0.0, total-cost: 264469.8]
                                           -- ASSIGN  |PARTITIONED|
-                                            exchange [cardinality: 30.16, op-cost: 0.0, total-cost: 264479.97]
+                                            exchange [cardinality: 29.05, op-cost: 0.0, total-cost: 264469.8]
                                             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                               group by ([$$250 := $$200]) decor ([$$244]) {
                                                         aggregate [$$181] <- [listify($$215)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
@@ -51,27 +51,27 @@
                                                           -- AGGREGATE  |LOCAL|
                                                             nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                                             -- NESTED_TUPLE_SOURCE  |LOCAL|
-                                                     } [cardinality: 0.0, op-cost: 0.0, total-cost: 264479.97]
+                                                     } [cardinality: 0.0, op-cost: 0.0, total-cost: 264469.8]
                                               -- PRE_CLUSTERED_GROUP_BY[$$200]  |PARTITIONED|
-                                                exchange [cardinality: 30.16, op-cost: 0.0, total-cost: 264479.97]
+                                                exchange [cardinality: 29.05, op-cost: 0.0, total-cost: 264469.8]
                                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                                  order (ASC, $$200) [cardinality: 30.16, op-cost: 0.0, total-cost: 264479.97]
+                                                  order (ASC, $$200) [cardinality: 29.05, op-cost: 0.0, total-cost: 264469.8]
                                                   -- STABLE_SORT [$$200(ASC)]  |PARTITIONED|
-                                                    exchange [cardinality: 30.16, op-cost: 0.0, total-cost: 264479.97]
+                                                    exchange [cardinality: 29.05, op-cost: 0.0, total-cost: 264469.8]
                                                     -- HASH_PARTITION_EXCHANGE [$$200]  |PARTITIONED|
-                                                      project ([$$244, $$208, $$200]) [cardinality: 30.16, op-cost: 0.0, total-cost: 264479.97]
+                                                      project ([$$244, $$208, $$200]) [cardinality: 29.05, op-cost: 0.0, total-cost: 264469.8]
                                                       -- STREAM_PROJECT  |PARTITIONED|
-                                                        exchange [cardinality: 30.16, op-cost: 0.0, total-cost: 264479.97]
+                                                        exchange [cardinality: 29.05, op-cost: 0.0, total-cost: 264469.8]
                                                         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                                          join (and(eq($$218, $$213), eq($$223, $$212))) [cardinality: 30.16, op-cost: 250.8, total-cost: 264479.97]
+                                                          join (and(eq($$218, $$213), eq($$223, $$212))) [cardinality: 29.05, op-cost: 172.16, total-cost: 264469.8]
                                                           -- HYBRID_HASH_JOIN [$$213, $$223][$$218, $$212]  |PARTITIONED|
-                                                            exchange [cardinality: 987.0, op-cost: 0.0, total-cost: 1000.0]
-                                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                            exchange [cardinality: 987.0, op-cost: 147.16, total-cost: 1147.16]
+                                                            -- HASH_PARTITION_EXCHANGE [$$213, $$223]  |PARTITIONED|
                                                               assign [$$223] <- [$$s2.getField(3)] project: [$$244, $$208, $$200, $$213, $$223] [cardinality: 987.0, op-cost: 0.0, total-cost: 1000.0]
                                                               -- ASSIGN  |PARTITIONED|
                                                                 project ([$$208, $$213, $$244, $$200, $$s2]) [cardinality: 987.0, op-cost: 0.0, total-cost: 1000.0]
                                                                 -- STREAM_PROJECT  |PARTITIONED|
-                                                                  exchange [cardinality: 987.0, op-cost: 0.0, total-cost: 1000.0]
+                                                                  exchange [cardinality: 987.0, op-cost: 147.16, total-cost: 1147.16]
                                                                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                                                     unnest-map [$$211, $$s2] <- index-search("supplier", 0, "Default", "tpch", "supplier", true, true, 1, $$210, 1, $$210, true, true, true) [cardinality: 987.0, op-cost: 1000.0, total-cost: 1000.0]
                                                                     -- BTREE_SEARCH  |PARTITIONED|
@@ -153,11 +153,11 @@
                                                                                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                                                                                   empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                                                                                   -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                                                            exchange [cardinality: 25.0, op-cost: 100.0, total-cost: 125.0]
-                                                            -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                                            exchange [cardinality: 25.0, op-cost: 25.0, total-cost: 50.0]
+                                                            -- HASH_PARTITION_EXCHANGE [$$218, $$212]  |PARTITIONED|
                                                               assign [$$218] <- [$$n2.getField(2)] project: [$$218, $$212] [cardinality: 25.0, op-cost: 0.0, total-cost: 25.0]
                                                               -- ASSIGN  |PARTITIONED|
-                                                                exchange [cardinality: 25.0, op-cost: 100.0, total-cost: 125.0]
+                                                                exchange [cardinality: 25.0, op-cost: 25.0, total-cost: 50.0]
                                                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                                                   replicate [cardinality: 25.0, op-cost: 0.0, total-cost: 25.0]
                                                                   -- REPLICATE  |PARTITIONED|
@@ -169,13 +169,13 @@
                                                                         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                                                           empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                                                           -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                                        exchange [cardinality: 80075.26, op-cost: 0.0, total-cost: 168971.26]
+                                        exchange [cardinality: 78142.08, op-cost: 0.0, total-cost: 168971.26]
                                         -- HASH_PARTITION_EXCHANGE [$$203]  |PARTITIONED|
-                                          project ([$$237, $$239, $$245, $$246, $$247, $$225, $$203, $$202]) [cardinality: 80075.26, op-cost: 0.0, total-cost: 168971.26]
+                                          project ([$$237, $$239, $$245, $$246, $$247, $$225, $$203, $$202]) [cardinality: 78142.08, op-cost: 0.0, total-cost: 168971.26]
                                           -- STREAM_PROJECT  |PARTITIONED|
-                                            exchange [cardinality: 80075.26, op-cost: 0.0, total-cost: 168971.26]
+                                            exchange [cardinality: 78142.08, op-cost: 0.0, total-cost: 168971.26]
                                             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                              join (eq($$201, $$204)) [cardinality: 80075.26, op-cost: 84023.26, total-cost: 168971.26]
+                                              join (eq($$201, $$204)) [cardinality: 78142.08, op-cost: 84023.26, total-cost: 168971.26]
                                               -- HYBRID_HASH_JOIN [$$204][$$201]  |PARTITIONED|
                                                 exchange [cardinality: 80000.0, op-cost: 0.0, total-cost: 80000.0]
                                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.07.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.07.plan
index 91cea89..033cd54 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.07.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.07.plan
@@ -1,46 +1,46 @@
-distribute result [$$149] [cardinality: 25.0, op-cost: 0.0, total-cost: 1555771.69]
+distribute result [$$149] [cardinality: 25.0, op-cost: 0.0, total-cost: 1552822.75]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 1555771.69]
+  exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 1552822.75]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$149] <- [{"$1": 5, "n_name": $$n_name, "revenue": $$165}] project: [$$149] [cardinality: 25.0, op-cost: 0.0, total-cost: 1555771.69]
+    assign [$$149] <- [{"$1": 5, "n_name": $$n_name, "revenue": $$165}] project: [$$149] [cardinality: 25.0, op-cost: 0.0, total-cost: 1552822.75]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 1555771.69]
+      exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 1552822.75]
       -- SORT_MERGE_EXCHANGE [$$165(DESC) ]  |PARTITIONED|
-        order (DESC, $$165) [cardinality: 25.0, op-cost: 116.1, total-cost: 1555771.69]
+        order (DESC, $$165) [cardinality: 25.0, op-cost: 116.1, total-cost: 1552822.75]
         -- STABLE_SORT [$$165(DESC)]  |PARTITIONED|
-          exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 1555655.59]
+          exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 1552706.65]
           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
             group by ([$$n_name := $$176]) decor ([]) {
                       aggregate [$$165] <- [global-sql-sum-serial($$175)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                       -- AGGREGATE  |LOCAL|
                         nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                         -- NESTED_TUPLE_SOURCE  |LOCAL|
-                   } [cardinality: 25.0, op-cost: 34190.86, total-cost: 1555655.59]
+                   } [cardinality: 25.0, op-cost: 32681.0, total-cost: 1552706.65]
             -- EXTERNAL_GROUP_BY[$$176]  |PARTITIONED|
-              exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 1521464.73]
+              exchange [cardinality: 25.0, op-cost: 0.0, total-cost: 1520025.65]
               -- HASH_PARTITION_EXCHANGE [$$176]  |PARTITIONED|
                 group by ([$$176 := $$150]) decor ([]) {
                           aggregate [$$175] <- [local-sql-sum-serial(numeric-multiply($$173, numeric-subtract(1, $$174)))] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                           -- AGGREGATE  |LOCAL|
                             nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                             -- NESTED_TUPLE_SOURCE  |LOCAL|
-                       } [cardinality: 25.0, op-cost: 34190.86, total-cost: 1521464.73]
+                       } [cardinality: 25.0, op-cost: 32681.0, total-cost: 1520025.65]
                 -- EXTERNAL_GROUP_BY[$$150]  |PARTITIONED|
-                  exchange [cardinality: 34190.86, op-cost: 0.0, total-cost: 1487273.87]
+                  exchange [cardinality: 32681.0, op-cost: 0.0, total-cost: 1487344.65]
                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                    project ([$$173, $$174, $$150]) [cardinality: 34190.86, op-cost: 0.0, total-cost: 1487273.87]
+                    project ([$$173, $$174, $$150]) [cardinality: 32681.0, op-cost: 0.0, total-cost: 1487344.65]
                     -- STREAM_PROJECT  |PARTITIONED|
-                      exchange [cardinality: 34190.86, op-cost: 0.0, total-cost: 1487273.87]
+                      exchange [cardinality: 32681.0, op-cost: 0.0, total-cost: 1487344.65]
                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                        join (and(eq($$154, $$160), eq($$170, $$159))) [cardinality: 34190.86, op-cost: 21043.43, total-cost: 1487273.87]
+                        join (and(eq($$154, $$160), eq($$170, $$159))) [cardinality: 32681.0, op-cost: 21059.51, total-cost: 1487344.65]
                         -- HYBRID_HASH_JOIN [$$160, $$170][$$154, $$159]  |PARTITIONED|
-                          exchange [cardinality: 17095.43, op-cost: 0.0, total-cost: 1461282.44]
+                          exchange [cardinality: 17111.51, op-cost: 0.0, total-cost: 1461337.14]
                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                            project ([$$173, $$174, $$150, $$160, $$170]) [cardinality: 17095.43, op-cost: 0.0, total-cost: 1461282.44]
+                            project ([$$173, $$174, $$150, $$160, $$170]) [cardinality: 17111.51, op-cost: 0.0, total-cost: 1461337.14]
                             -- STREAM_PROJECT  |PARTITIONED|
-                              exchange [cardinality: 17095.43, op-cost: 0.0, total-cost: 1461282.44]
+                              exchange [cardinality: 17111.51, op-cost: 0.0, total-cost: 1461337.14]
                               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                join (eq($$158, $$156)) [cardinality: 17095.43, op-cost: 618200.06, total-cost: 1461282.44]
+                                join (eq($$158, $$156)) [cardinality: 17111.51, op-cost: 618216.11, total-cost: 1461337.14]
                                 -- HYBRID_HASH_JOIN [$$158][$$156]  |PARTITIONED|
                                   exchange [cardinality: 600572.0, op-cost: 0.0, total-cost: 600572.0]
                                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
@@ -56,13 +56,13 @@
                                             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                               empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                               -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                                  exchange [cardinality: 4265.77, op-cost: 17063.08, total-cost: 242510.39]
+                                  exchange [cardinality: 4269.78, op-cost: 17079.13, total-cost: 242549.04]
                                   -- BROADCAST_EXCHANGE  |PARTITIONED|
-                                    project ([$$150, $$160, $$156]) [cardinality: 4265.77, op-cost: 0.0, total-cost: 225447.31]
+                                    project ([$$150, $$160, $$156]) [cardinality: 4269.78, op-cost: 0.0, total-cost: 225469.91]
                                     -- STREAM_PROJECT  |PARTITIONED|
-                                      exchange [cardinality: 4265.77, op-cost: 17063.08, total-cost: 242510.39]
+                                      exchange [cardinality: 4269.78, op-cost: 17079.13, total-cost: 242549.04]
                                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                        join (eq($$155, $$167)) [cardinality: 4265.77, op-cost: 33318.91, total-cost: 225447.31]
+                                        join (eq($$155, $$167)) [cardinality: 4269.78, op-cost: 33330.21, total-cost: 225469.91]
                                         -- HYBRID_HASH_JOIN [$$167][$$155]  |PARTITIONED|
                                           exchange [cardinality: 21307.62, op-cost: 0.0, total-cost: 150000.0]
                                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
@@ -78,13 +78,13 @@
                                                     -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                                       empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                                       -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                                          exchange [cardinality: 3002.82, op-cost: 12011.29, total-cost: 42128.4]
+                                          exchange [cardinality: 3005.65, op-cost: 12022.59, total-cost: 42139.7]
                                           -- BROADCAST_EXCHANGE  |PARTITIONED|
-                                            project ([$$150, $$160, $$155]) [cardinality: 3002.82, op-cost: 0.0, total-cost: 30117.11]
+                                            project ([$$150, $$160, $$155]) [cardinality: 3005.65, op-cost: 0.0, total-cost: 30117.11]
                                             -- STREAM_PROJECT  |PARTITIONED|
-                                              exchange [cardinality: 3002.82, op-cost: 12011.29, total-cost: 42128.4]
+                                              exchange [cardinality: 3005.65, op-cost: 12022.59, total-cost: 42139.7]
                                               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                                join (eq($$153, $$160)) [cardinality: 3002.82, op-cost: 15034.11, total-cost: 30117.11]
+                                                join (eq($$153, $$160)) [cardinality: 3005.65, op-cost: 15034.11, total-cost: 30117.11]
                                                 -- HYBRID_HASH_JOIN [$$153][$$160]  |PARTITIONED|
                                                   exchange [cardinality: 15000.0, op-cost: 0.0, total-cost: 15000.0]
                                                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.08.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.08.plan
index 1a2110b..0c0f2e9 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.08.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.08.plan
@@ -1,48 +1,48 @@
-distribute result [$$186] [cardinality: 2.52, op-cost: 0.0, total-cost: 988897.43]
+distribute result [$$186] [cardinality: 2.52, op-cost: 0.0, total-cost: 981839.91]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 2.52, op-cost: 0.0, total-cost: 988897.43]
+  exchange [cardinality: 2.52, op-cost: 0.0, total-cost: 981839.91]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$186] <- [{"$1": 7, "supp_nation": $$supp_nation, "cust_nation": $$cust_nation, "l_year": $$l_year, "revenue": $$200}] project: [$$186] [cardinality: 2.52, op-cost: 0.0, total-cost: 988897.43]
+    assign [$$186] <- [{"$1": 7, "supp_nation": $$supp_nation, "cust_nation": $$cust_nation, "l_year": $$l_year, "revenue": $$200}] project: [$$186] [cardinality: 2.52, op-cost: 0.0, total-cost: 981839.91]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 2.52, op-cost: 0.0, total-cost: 988897.43]
+      exchange [cardinality: 2.52, op-cost: 0.0, total-cost: 981839.91]
       -- SORT_MERGE_EXCHANGE [$$supp_nation(ASC), $$cust_nation(ASC), $$l_year(ASC) ]  |PARTITIONED|
-        order (ASC, $$supp_nation) (ASC, $$cust_nation) (ASC, $$l_year) [cardinality: 2.52, op-cost: 3.36, total-cost: 988897.43]
+        order (ASC, $$supp_nation) (ASC, $$cust_nation) (ASC, $$l_year) [cardinality: 2.52, op-cost: 3.36, total-cost: 981839.91]
         -- STABLE_SORT [$$supp_nation(ASC), $$cust_nation(ASC), $$l_year(ASC)]  |PARTITIONED|
-          exchange [cardinality: 2.52, op-cost: 0.0, total-cost: 988894.07]
+          exchange [cardinality: 2.52, op-cost: 0.0, total-cost: 981836.55]
           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
             group by ([$$supp_nation := $$217; $$cust_nation := $$218; $$l_year := $$219]) decor ([]) {
                       aggregate [$$200] <- [global-sql-sum-serial($$216)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                       -- AGGREGATE  |LOCAL|
                         nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                         -- NESTED_TUPLE_SOURCE  |LOCAL|
-                   } [cardinality: 2.52, op-cost: 577.39, total-cost: 988894.07]
+                   } [cardinality: 2.52, op-cost: 552.41, total-cost: 981836.55]
             -- EXTERNAL_GROUP_BY[$$217, $$218, $$219]  |PARTITIONED|
-              exchange [cardinality: 2.52, op-cost: 0.0, total-cost: 988316.68]
+              exchange [cardinality: 2.52, op-cost: 0.0, total-cost: 981284.14]
               -- HASH_PARTITION_EXCHANGE [$$217, $$218, $$219]  |PARTITIONED|
                 group by ([$$217 := $$191; $$218 := $$192; $$219 := $$214]) decor ([]) {
                           aggregate [$$216] <- [local-sql-sum-serial($$184)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                           -- AGGREGATE  |LOCAL|
                             nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                             -- NESTED_TUPLE_SOURCE  |LOCAL|
-                       } [cardinality: 2.52, op-cost: 577.39, total-cost: 988316.68]
+                       } [cardinality: 2.52, op-cost: 552.41, total-cost: 981284.14]
                 -- EXTERNAL_GROUP_BY[$$191, $$192, $$214]  |PARTITIONED|
-                  exchange [cardinality: 577.39, op-cost: 0.0, total-cost: 987739.29]
+                  exchange [cardinality: 552.41, op-cost: 0.0, total-cost: 980731.73]
                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                    assign [$$184, $$214] <- [numeric-multiply($$211, numeric-subtract(1, $$212)), get-year(date($$190))] project: [$$184, $$191, $$192, $$214] [cardinality: 577.39, op-cost: 0.0, total-cost: 987739.29]
+                    assign [$$184, $$214] <- [numeric-multiply($$211, numeric-subtract(1, $$212)), get-year(date($$190))] project: [$$184, $$191, $$192, $$214] [cardinality: 552.41, op-cost: 0.0, total-cost: 980731.73]
                     -- ASSIGN  |PARTITIONED|
-                      project ([$$191, $$192, $$211, $$212, $$190]) [cardinality: 577.39, op-cost: 0.0, total-cost: 987739.29]
+                      project ([$$191, $$192, $$211, $$212, $$190]) [cardinality: 552.41, op-cost: 0.0, total-cost: 980731.73]
                       -- STREAM_PROJECT  |PARTITIONED|
-                        exchange [cardinality: 577.39, op-cost: 0.0, total-cost: 987739.29]
+                        exchange [cardinality: 552.41, op-cost: 0.0, total-cost: 980731.73]
                         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                          join (and(eq($$201, $$199), eq($$197, $$210))) [cardinality: 577.39, op-cost: 29434.39, total-cost: 987739.29]
+                          join (and(eq($$201, $$199), eq($$197, $$210))) [cardinality: 552.41, op-cost: 28797.6, total-cost: 980731.73]
                           -- HYBRID_HASH_JOIN [$$199, $$210][$$201, $$197]  |PARTITIONED|
-                            exchange [cardinality: 150141.11, op-cost: 14420.28, total-cost: 164420.28]
+                            exchange [cardinality: 150141.11, op-cost: 13783.49, total-cost: 163783.49]
                             -- HASH_PARTITION_EXCHANGE [$$199, $$210]  |PARTITIONED|
                               assign [$$210] <- [$$o.getField(1)] project: [$$191, $$192, $$211, $$212, $$190, $$199, $$210] [cardinality: 150141.11, op-cost: 0.0, total-cost: 150000.0]
                               -- ASSIGN  |PARTITIONED|
                                 project ([$$212, $$211, $$190, $$191, $$192, $$199, $$o]) [cardinality: 150141.11, op-cost: 0.0, total-cost: 150000.0]
                                 -- STREAM_PROJECT  |PARTITIONED|
-                                  exchange [cardinality: 150141.11, op-cost: 14420.28, total-cost: 164420.28]
+                                  exchange [cardinality: 150141.11, op-cost: 13783.49, total-cost: 163783.49]
                                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                     unnest-map [$$196, $$o] <- index-search("orders", 0, "Default", "tpch", "orders", true, true, 1, $$195, 1, $$195, true, true, true) [cardinality: 150141.11, op-cost: 150000.0, total-cost: 150000.0]
                                     -- BTREE_SEARCH  |PARTITIONED|
@@ -56,7 +56,7 @@
                                             -- STREAM_PROJECT  |PARTITIONED|
                                               exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                                join (eq($$193, $$204)) [cardinality: 14406.71, op-cost: 180543.69, total-cost: 784210.07]
+                                                join (eq($$193, $$204)) [cardinality: 13770.52, op-cost: 180539.58, total-cost: 784201.87]
                                                 -- HYBRID_HASH_JOIN [$$204][$$193]  |PARTITIONED|
                                                   exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
@@ -80,7 +80,7 @@
                                                     -- STREAM_PROJECT  |PARTITIONED|
                                                       exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                                        join (eq($$205, $$198)) [cardinality: 78.9, op-cost: 995.4, total-cost: 2778.8]
+                                                        join (eq($$205, $$198)) [cardinality: 77.87, op-cost: 995.4, total-cost: 2778.8]
                                                         -- HYBRID_HASH_JOIN [$$205][$$198]  |PARTITIONED|
                                                           exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.09.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.09.plan
index a22503e..e8a97fa 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.09.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.09.plan
@@ -1,36 +1,36 @@
-distribute result [$$199] [cardinality: 2.0, op-cost: 0.0, total-cost: 1258961.42]
+distribute result [$$199] [cardinality: 2.0, op-cost: 0.0, total-cost: 1258937.81]
 -- DISTRIBUTE_RESULT  |PARTITIONED|
-  exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 1258961.42]
+  exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 1258937.81]
   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-    assign [$$199] <- [{"$1": 8, "o_year": $$o_year, "mkt_share": numeric-divide($$214, $$215)}] project: [$$199] [cardinality: 2.0, op-cost: 0.0, total-cost: 1258961.42]
+    assign [$$199] <- [{"$1": 8, "o_year": $$o_year, "mkt_share": numeric-divide($$214, $$215)}] project: [$$199] [cardinality: 2.0, op-cost: 0.0, total-cost: 1258937.81]
     -- ASSIGN  |PARTITIONED|
-      exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 1258961.42]
+      exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 1258937.81]
       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
         group by ([$$o_year := $$238]) decor ([]) {
                   aggregate [$$214, $$215] <- [global-sql-sum-serial($$236), global-sql-sum-serial($$237)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                   -- AGGREGATE  |LOCAL|
                     nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                     -- NESTED_TUPLE_SOURCE  |LOCAL|
-               } [cardinality: 2.0, op-cost: 167.75, total-cost: 1258961.42]
+               } [cardinality: 2.0, op-cost: 160.49, total-cost: 1258937.81]
         -- EXTERNAL_GROUP_BY[$$238]  |PARTITIONED|
-          exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 1258793.67]
+          exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 1258777.32]
           -- HASH_PARTITION_EXCHANGE [$$238]  |PARTITIONED|
             group by ([$$238 := $$200]) decor ([]) {
                       aggregate [$$236, $$237] <- [local-sql-sum-serial(switch-case(true, eq($$232, "PERU"), numeric-multiply($$230, numeric-subtract(1, $$231)), 0)), local-sql-sum-serial(numeric-multiply($$230, numeric-subtract(1, $$231)))] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                       -- AGGREGATE  |LOCAL|
                         nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                         -- NESTED_TUPLE_SOURCE  |LOCAL|
-                   } [cardinality: 2.0, op-cost: 167.75, total-cost: 1258793.67]
+                   } [cardinality: 2.0, op-cost: 160.49, total-cost: 1258777.32]
             -- EXTERNAL_GROUP_BY[$$200]  |PARTITIONED|
-              exchange [cardinality: 167.75, op-cost: 0.0, total-cost: 1258625.92]
+              exchange [cardinality: 160.49, op-cost: 0.0, total-cost: 1258616.83]
               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                assign [$$200] <- [get-year(date($$201))] project: [$$232, $$230, $$231, $$200] [cardinality: 167.75, op-cost: 0.0, total-cost: 1258625.92]
+                assign [$$200] <- [get-year(date($$201))] project: [$$232, $$230, $$231, $$200] [cardinality: 160.49, op-cost: 0.0, total-cost: 1258616.83]
                 -- ASSIGN  |PARTITIONED|
-                  project ([$$230, $$231, $$201, $$232]) [cardinality: 167.75, op-cost: 0.0, total-cost: 1258625.92]
+                  project ([$$230, $$231, $$201, $$232]) [cardinality: 160.49, op-cost: 0.0, total-cost: 1258616.83]
                   -- STREAM_PROJECT  |PARTITIONED|
-                    exchange [cardinality: 167.75, op-cost: 0.0, total-cost: 1258625.92]
+                    exchange [cardinality: 160.49, op-cost: 0.0, total-cost: 1258616.83]
                     -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                      join (eq($$216, $$209)) [cardinality: 167.75, op-cost: 267.75, total-cost: 1258625.92]
+                      join (eq($$216, $$209)) [cardinality: 160.49, op-cost: 262.6, total-cost: 1258616.83]
                       -- HYBRID_HASH_JOIN [$$216][$$209]  |PARTITIONED|
                         exchange [cardinality: 987.0, op-cost: 0.0, total-cost: 1000.0]
                         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
@@ -52,7 +52,7 @@
                                         -- STREAM_PROJECT  |PARTITIONED|
                                           exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                           -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                            join (and(eq($$212, $$210), eq($$218, $$208))) [cardinality: 167.75, op-cost: 938.74, total-cost: 1256721.24]
+                                            join (and(eq($$212, $$210), eq($$218, $$208))) [cardinality: 167.9, op-cost: 938.74, total-cost: 1256721.24]
                                             -- HYBRID_HASH_JOIN [$$210, $$218][$$212, $$208]  |PARTITIONED|
                                               exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.10.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.10.plan
index ca1e816..5013a2b 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.10.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.10.plan
@@ -1,22 +1,22 @@
-distribute result [$$122] [cardinality: 26629.31, op-cost: 0.0, total-cost: 1670441.71]
+distribute result [$$122] [cardinality: 25648.6, op-cost: 0.0, total-cost: 1660495.02]
 -- DISTRIBUTE_RESULT  |UNPARTITIONED|
-  exchange [cardinality: 26629.31, op-cost: 0.0, total-cost: 1670441.71]
+  exchange [cardinality: 25648.6, op-cost: 0.0, total-cost: 1660495.02]
   -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
-    assign [$$122] <- [{"$1": 9, "$2": $$140}] project: [$$122] [cardinality: 26629.31, op-cost: 0.0, total-cost: 1670441.71]
+    assign [$$122] <- [{"$1": 9, "$2": $$140}] project: [$$122] [cardinality: 25648.6, op-cost: 0.0, total-cost: 1660495.02]
     -- ASSIGN  |UNPARTITIONED|
-      aggregate [$$140] <- [agg-sql-sum($$144)] [cardinality: 26629.31, op-cost: 0.0, total-cost: 1670441.71]
+      aggregate [$$140] <- [agg-sql-sum($$144)] [cardinality: 25648.6, op-cost: 0.0, total-cost: 1660495.02]
       -- AGGREGATE  |UNPARTITIONED|
-        exchange [cardinality: 26629.31, op-cost: 0.0, total-cost: 1670441.71]
+        exchange [cardinality: 25648.6, op-cost: 0.0, total-cost: 1660495.02]
         -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
-          aggregate [$$144] <- [agg-sql-count(1)] [cardinality: 26629.31, op-cost: 0.0, total-cost: 1670441.71]
+          aggregate [$$144] <- [agg-sql-count(1)] [cardinality: 25648.6, op-cost: 0.0, total-cost: 1660495.02]
           -- AGGREGATE  |PARTITIONED|
-            exchange [cardinality: 26629.31, op-cost: 0.0, total-cost: 1670441.71]
+            exchange [cardinality: 25648.6, op-cost: 0.0, total-cost: 1660495.02]
             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-              unnest-map [$$136, $$o] <- index-search("orders", 0, "Default", "tpch", "orders", true, true, 1, $$132, 1, $$132, true, true, true) [cardinality: 26629.31, op-cost: 133021.33, total-cost: 1670441.71]
+              unnest-map [$$136, $$o] <- index-search("orders", 0, "Default", "tpch", "orders", true, true, 1, $$132, 1, $$132, true, true, true) [cardinality: 25648.6, op-cost: 128122.39, total-cost: 1660495.02]
               -- BTREE_SEARCH  |PARTITIONED|
                 exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                  order (ASC, $$132) [cardinality: 26629.31, op-cost: 133021.33, total-cost: 1670441.71]
+                  order (ASC, $$132) [cardinality: 25648.6, op-cost: 128122.39, total-cost: 1660495.02]
                   -- STABLE_SORT [$$132(ASC)]  |PARTITIONED|
                     exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                     -- HASH_PARTITION_EXCHANGE [$$132]  |PARTITIONED|
@@ -24,7 +24,7 @@
                       -- STREAM_PROJECT  |PARTITIONED|
                         exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                          join (and(eq($$135, $$128), eq($$134, $$125))) [cardinality: 26604.26, op-cost: 615299.08, total-cost: 1431003.34]
+                          join (and(eq($$135, $$128), eq($$134, $$125))) [cardinality: 25624.47, op-cost: 614777.52, total-cost: 1429874.73]
                           -- HYBRID_HASH_JOIN [$$128, $$125][$$135, $$134]  |PARTITIONED|
                             exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
@@ -46,7 +46,7 @@
                               -- STREAM_PROJECT  |PARTITIONED|
                                 exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                  join (eq($$138, $$137)) [cardinality: 3540.53, op-cost: 3640.53, total-cost: 200970.15]
+                                  join (eq($$138, $$137)) [cardinality: 3410.13, op-cost: 3555.05, total-cost: 200884.68]
                                   -- HYBRID_HASH_JOIN [$$138][$$137]  |PARTITIONED|
                                     exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                     -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
@@ -54,7 +54,7 @@
                                       -- STREAM_PROJECT  |PARTITIONED|
                                         exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                          join (eq($$130, $$135)) [cardinality: 3540.53, op-cost: 4527.53, total-cost: 197204.63]
+                                          join (eq($$130, $$135)) [cardinality: 3455.05, op-cost: 4527.53, total-cost: 197204.63]
                                           -- HYBRID_HASH_JOIN [$$135][$$130]  |PARTITIONED|
                                             exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                             -- HASH_PARTITION_EXCHANGE [$$135]  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.11.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.11.plan
index ca45dac..84c8bbf 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.11.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/tpch/query-plans/query-plans.11.plan
@@ -1,44 +1,44 @@
-distribute result [$$185] [cardinality: 4436.55, op-cost: 0.0, total-cost: 990428.16]
+distribute result [$$185] [cardinality: 4440.72, op-cost: 0.0, total-cost: 990469.45]
 -- DISTRIBUTE_RESULT  |UNPARTITIONED|
-  exchange [cardinality: 4436.55, op-cost: 0.0, total-cost: 990428.16]
+  exchange [cardinality: 4440.72, op-cost: 0.0, total-cost: 990469.45]
   -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
-    limit 20 [cardinality: 4436.55, op-cost: 0.0, total-cost: 990428.16]
+    limit 20 [cardinality: 4440.72, op-cost: 0.0, total-cost: 990469.45]
     -- STREAM_LIMIT  |UNPARTITIONED|
-      assign [$$185] <- [{"$1": 10, "c_custkey": $$c_custkey, "c_name": $$c_name, "revenue": $$203, "c_acctbal": $$c_acctbal, "n_name": $$n_name, "c_address": $$c_address, "c_phone": $$c_phone, "c_comment": $$c_comment}] project: [$$185] [cardinality: 4436.55, op-cost: 0.0, total-cost: 990428.16]
+      assign [$$185] <- [{"$1": 10, "c_custkey": $$c_custkey, "c_name": $$c_name, "revenue": $$203, "c_acctbal": $$c_acctbal, "n_name": $$n_name, "c_address": $$c_address, "c_phone": $$c_phone, "c_comment": $$c_comment}] project: [$$185] [cardinality: 4440.72, op-cost: 0.0, total-cost: 990469.45]
       -- ASSIGN  |PARTITIONED|
-        exchange [cardinality: 4436.55, op-cost: 0.0, total-cost: 990428.16]
+        exchange [cardinality: 4440.72, op-cost: 0.0, total-cost: 990469.45]
         -- SORT_MERGE_EXCHANGE [$$203(DESC) ]  |PARTITIONED|
-          limit 20 [cardinality: 4436.55, op-cost: 0.0, total-cost: 990428.16]
+          limit 20 [cardinality: 4440.72, op-cost: 0.0, total-cost: 990469.45]
           -- STREAM_LIMIT  |PARTITIONED|
-            exchange [cardinality: 4436.55, op-cost: 0.0, total-cost: 990428.16]
+            exchange [cardinality: 4440.72, op-cost: 0.0, total-cost: 990469.45]
             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-              order (topK: 20) (DESC, $$203) [cardinality: 4436.55, op-cost: 0.0, total-cost: 990428.16]
+              order (topK: 20) (DESC, $$203) [cardinality: 4440.72, op-cost: 0.0, total-cost: 990469.45]
               -- STABLE_SORT [topK: 20] [$$203(DESC)]  |PARTITIONED|
-                exchange [cardinality: 4436.55, op-cost: 0.0, total-cost: 990428.16]
+                exchange [cardinality: 4440.72, op-cost: 0.0, total-cost: 990469.45]
                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                   group by ([$$c_custkey := $$211; $$c_name := $$212; $$c_acctbal := $$213; $$c_phone := $$214; $$n_name := $$215; $$c_address := $$216; $$c_comment := $$217]) decor ([]) {
                             aggregate [$$203] <- [global-sql-sum-serial($$210)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                             -- AGGREGATE  |LOCAL|
                               nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                               -- NESTED_TUPLE_SOURCE  |LOCAL|
-                         } [cardinality: 4436.55, op-cost: 4436.55, total-cost: 990428.16]
+                         } [cardinality: 4440.72, op-cost: 4440.72, total-cost: 990469.45]
                   -- EXTERNAL_GROUP_BY[$$211, $$212, $$213, $$214, $$215, $$216, $$217]  |PARTITIONED|
-                    exchange [cardinality: 4436.55, op-cost: 0.0, total-cost: 985991.61]
+                    exchange [cardinality: 4440.72, op-cost: 0.0, total-cost: 986028.73]
                     -- HASH_PARTITION_EXCHANGE [$$211, $$212, $$213, $$214, $$215, $$216, $$217]  |PARTITIONED|
                       group by ([$$211 := $$195; $$212 := $$187; $$213 := $$188; $$214 := $$189; $$215 := $$190; $$216 := $$191; $$217 := $$192]) decor ([]) {
                                 aggregate [$$210] <- [local-sql-sum-serial(numeric-multiply($$208, numeric-subtract(1, $$209)))] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                 -- AGGREGATE  |LOCAL|
                                   nested tuple source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                   -- NESTED_TUPLE_SOURCE  |LOCAL|
-                             } [cardinality: 4436.55, op-cost: 4436.55, total-cost: 985991.61]
+                             } [cardinality: 4440.72, op-cost: 4440.72, total-cost: 986028.73]
                       -- EXTERNAL_GROUP_BY[$$195, $$187, $$188, $$189, $$190, $$191, $$192]  |PARTITIONED|
-                        exchange [cardinality: 4436.55, op-cost: 0.0, total-cost: 981555.06]
+                        exchange [cardinality: 4440.72, op-cost: 0.0, total-cost: 981588.01]
                         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                          project ([$$208, $$209, $$195, $$187, $$188, $$189, $$190, $$191, $$192]) [cardinality: 4436.55, op-cost: 0.0, total-cost: 981555.06]
+                          project ([$$208, $$209, $$195, $$187, $$188, $$189, $$190, $$191, $$192]) [cardinality: 4440.72, op-cost: 0.0, total-cost: 981588.01]
                           -- STREAM_PROJECT  |PARTITIONED|
-                            exchange [cardinality: 4436.55, op-cost: 0.0, total-cost: 981555.06]
+                            exchange [cardinality: 4440.72, op-cost: 0.0, total-cost: 981588.01]
                             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                              join (eq($$198, $$196)) [cardinality: 4436.55, op-cost: 169494.26, total-cost: 981555.06]
+                              join (eq($$198, $$196)) [cardinality: 4440.72, op-cost: 169510.74, total-cost: 981588.01]
                               -- HYBRID_HASH_JOIN [$$198][$$196]  |PARTITIONED|
                                 exchange [cardinality: 151979.18, op-cost: 0.0, total-cost: 600572.0]
                                 -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
@@ -56,13 +56,13 @@
                                             -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
                                               empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
                                               -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
-                                exchange [cardinality: 4378.77, op-cost: 17515.08, total-cost: 211488.8]
+                                exchange [cardinality: 4382.89, op-cost: 17531.56, total-cost: 211505.28]
                                 -- BROADCAST_EXCHANGE  |PARTITIONED|
-                                  project ([$$195, $$187, $$188, $$189, $$190, $$191, $$192, $$196]) [cardinality: 4378.77, op-cost: 0.0, total-cost: 193973.72]
+                                  project ([$$195, $$187, $$188, $$189, $$190, $$191, $$192, $$196]) [cardinality: 4382.89, op-cost: 0.0, total-cost: 193973.72]
                                   -- STREAM_PROJECT  |PARTITIONED|
-                                    exchange [cardinality: 4378.77, op-cost: 17515.08, total-cost: 211488.8]
+                                    exchange [cardinality: 4382.89, op-cost: 17531.56, total-cost: 211505.28]
                                     -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                      join (eq($$201, $$199)) [cardinality: 4378.77, op-cost: 4478.77, total-cost: 193973.72]
+                                      join (eq($$201, $$199)) [cardinality: 4382.89, op-cost: 4478.77, total-cost: 193973.72]
                                       -- HYBRID_HASH_JOIN [$$201][$$199]  |PARTITIONED|
                                         exchange [cardinality: 15014.11, op-cost: 0.0, total-cost: 15000.0]
                                         -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|