[ASTERIXDB-3341][COMP]: CBO choosing bad indexes (CH2 Q8)
Change-Id: Iee097528ef3792bfb55636a140e2519b67bcc29e
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/18102
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: <murali.krishna@couchbase.com>
Reviewed-by: Vijay Sarathy <vijay.sarathy@couchbase.com>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java
index 27bae51..894fd93 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java
@@ -629,8 +629,8 @@
continue;
}
AbstractFunctionCallExpression argFuncExpr = (AbstractFunctionCallExpression) argExpr;
- boolean matchFound = analyzeFunctionExprAndUpdateAnalyzedAM(argFuncExpr, assignsAndUnnests, analyzedAMs,
- context, typeEnvironment);
+ boolean matchFound = analyzeSelectOrJoinOpConditionAndUpdateAnalyzedAM(argFuncExpr, assignsAndUnnests,
+ analyzedAMs, context, typeEnvironment);
found = found || matchFound;
}
return found;
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
index 187a8ee..980f281 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
@@ -420,7 +420,7 @@
return nextOp;
}
- private ILogicalOperator findSelectOrDataScan(ILogicalOperator op) {
+ private ILogicalOperator findSelectOrUnnestOrDataScan(ILogicalOperator op) {
LogicalOperatorTag tag;
ILogicalOperator currentOp = op;
while (true) {
@@ -431,7 +431,8 @@
if (tag == LogicalOperatorTag.EMPTYTUPLESOURCE) {
return null; // if this happens, there is nothing we can do in CBO code since there is no datasourcescan
}
- if ((tag == LogicalOperatorTag.SELECT) || (tag == LogicalOperatorTag.DATASOURCESCAN)) {
+ if ((tag == LogicalOperatorTag.SELECT) || (tag == LogicalOperatorTag.UNNEST)
+ || (tag == LogicalOperatorTag.DATASOURCESCAN)) {
return currentOp;
}
@@ -685,7 +686,7 @@
EmptyTupleSourceOperator etsOp = etsDataSource.first;
DataSourceScanOperator dataSourceOp = etsDataSource.second;
if (op.getOperatorTag().equals(LogicalOperatorTag.DISTRIBUTE_RESULT)) {// single table query
- ILogicalOperator selectOp = findSelectOrDataScan(op);
+ ILogicalOperator selectOp = findSelectOrUnnestOrDataScan(op);
if (selectOp == null) {
return false;
} else {
@@ -759,7 +760,7 @@
afcExpr.removeAnnotation(HashJoinExpressionAnnotation.class);
}
- private void setAnnotation(AbstractFunctionCallExpression afcExpr, IExpressionAnnotation anno) {
+ protected static void setAnnotation(AbstractFunctionCallExpression afcExpr, IExpressionAnnotation anno) {
FunctionIdentifier fi = afcExpr.getFunctionIdentifier();
List<Mutable<ILogicalExpression>> arguments = afcExpr.getArguments();
@@ -829,7 +830,7 @@
private void buildNewTree(PlanNode plan) {
ILogicalOperator leftInput = plan.getLeafInput();
skipAllIndexes(plan, leftInput);
- ILogicalOperator selOp = findSelectOrDataScan(leftInput);
+ ILogicalOperator selOp = findSelectOrUnnestOrDataScan(leftInput);
if (selOp != null) {
addCardCostAnnotations(selOp, plan);
}
@@ -936,7 +937,7 @@
// leaf
ILogicalOperator leftInput = leftPlan.getLeafInput();
skipAllIndexes(leftPlan, leftInput);
- ILogicalOperator selOp = findSelectOrDataScan(leftInput);
+ ILogicalOperator selOp = findSelectOrUnnestOrDataScan(leftInput);
if (selOp != null) {
addCardCostAnnotations(selOp, leftPlan);
}
@@ -956,7 +957,7 @@
// leaf
ILogicalOperator rightInput = rightPlan.getLeafInput();
skipAllIndexes(rightPlan, rightInput);
- ILogicalOperator selOp = findSelectOrDataScan(rightInput);
+ ILogicalOperator selOp = findSelectOrUnnestOrDataScan(rightInput);
if (selOp != null) {
addCardCostAnnotations(selOp, rightPlan);
}
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 7c7e20c..4d8ca67 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
@@ -967,7 +967,7 @@
return null;
}
- private boolean findUnnestOp(ILogicalOperator op) {
+ protected boolean findUnnestOp(ILogicalOperator op) {
ILogicalOperator currentOp = op;
while (currentOp != null && currentOp.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
if (currentOp.getOperatorTag().equals(LogicalOperatorTag.UNNEST)) {
@@ -995,13 +995,16 @@
ILogicalOperator leafInput = findLeafInput(vars);
SelectOperator selOp;
if (leafInput.getOperatorTag().equals(LogicalOperatorTag.SELECT)) {
- selOp = (SelectOperator) leafInput;
+ selOp = (SelectOperator) getStatsHandle().findSelectOpWithExpr(leafInput, exp);
+ if (selOp == null) {
+ selOp = (SelectOperator) leafInput;
+ }
} else {
selOp = new SelectOperator(new MutableObject<>(exp));
selOp.getInputs().add(new MutableObject<>(leafInput));
}
sel = getStatsHandle().findSelectivityForThisPredicate(selOp, (AbstractFunctionCallExpression) exp,
- findUnnestOp(leafInput));
+ findUnnestOp(selOp));
// Sometimes the sample query returns greater more rows than the sample size. Cap the selectivity to 0.9999
sel = Math.min(sel, 0.9999);
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinNode.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinNode.java
index b2ed5ed..52caaf3 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinNode.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinNode.java
@@ -617,7 +617,7 @@
if (IndexCostInfo.get(i).second == -1.0) {
AbstractFunctionCallExpression afce = IndexCostInfo.get(i).third;
// this index has to be skipped, so find the corresponding expression
- afce.putAnnotation(SkipSecondaryIndexSearchExpressionAnnotation
+ EnumerateJoinsRule.setAnnotation(afce, SkipSecondaryIndexSearchExpressionAnnotation
.newInstance(Collections.singleton(IndexCostInfo.get(i).first.getIndexName())));
}
}
@@ -650,13 +650,17 @@
sel = selectivityAnnotation.getSelectivity();
} else {
if (leafInput.getOperatorTag().equals(LogicalOperatorTag.SELECT)) {
- selOp = (SelectOperator) leafInput;
+ selOp = (SelectOperator) joinEnum.getStatsHandle().findSelectOpWithExpr(leafInput, afce);
+ if (selOp == null) {
+ selOp = (SelectOperator) leafInput;
+ }
} else {
selOp = new SelectOperator(new MutableObject<>(afce));
selOp.getInputs().add(new MutableObject<>(leafInput));
}
sel = joinEnum.getStatsHandle().findSelectivityForThisPredicate(selOp, afce,
- chosenIndex.getIndexType().equals(DatasetConfig.IndexType.ARRAY));
+ chosenIndex.getIndexType().equals(DatasetConfig.IndexType.ARRAY)
+ || joinEnum.findUnnestOp(selOp));
}
IndexCostInfo.add(new Triple<>(chosenIndex, sel, afce));
}
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 5d46f2e..63c67a5 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
@@ -313,7 +313,7 @@
}
private AggregateOperator findAggOp(ILogicalOperator op, ILogicalExpression exp) throws AlgebricksException {
- /*private final */ ContainsExpressionVisitor visitor = new ContainsExpressionVisitor();
+ ContainsExpressionVisitor visitor = new ContainsExpressionVisitor();
SubplanOperator subOp;
while (op != null && op.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
if (op.getOperatorTag().equals(LogicalOperatorTag.SUBPLAN)) {
@@ -327,104 +327,70 @@
return null;
}
- private SubplanOperator findSubplanWithExpr(ILogicalOperator op, ILogicalExpression exp)
+ protected SelectOperator findSelectOpWithExpr(ILogicalOperator op, ILogicalExpression exp)
throws AlgebricksException {
- /*private final */ ContainsExpressionVisitor visitor = new ContainsExpressionVisitor();
+ ContainsExpressionVisitor visitor = new ContainsExpressionVisitor();
SubplanOperator subOp;
- while (op != null && op.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
- if (op.getOperatorTag().equals(LogicalOperatorTag.SUBPLAN)) {
- subOp = (SubplanOperator) op;
- ILogicalOperator nextOp = subOp.getNestedPlans().get(0).getRoots().get(0).getValue();
-
- while (nextOp != null) {
+ ILogicalOperator currentOp = op;
+ while (currentOp != null && currentOp.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
+ if (currentOp.getOperatorTag().equals(LogicalOperatorTag.SELECT)) {
+ ILogicalOperator nextOp = currentOp.getInputs().get(0).getValue();
+ if (nextOp.getOperatorTag().equals(LogicalOperatorTag.SUBPLAN)) {
+ subOp = (SubplanOperator) nextOp;
+ ILogicalOperator childOp = subOp.getNestedPlans().get(0).getRoots().get(0).getValue();
+ while (childOp != null) {
+ visitor.setExpression(exp);
+ if (childOp.acceptExpressionTransform(visitor)) {
+ return (SelectOperator) currentOp;
+ }
+ if (childOp.getInputs().isEmpty()) {
+ break;
+ }
+ childOp = childOp.getInputs().get(0).getValue();
+ }
+ } else {
visitor.setExpression(exp);
- if (nextOp.acceptExpressionTransform(visitor)) {
- return subOp;
+ if (currentOp.acceptExpressionTransform(visitor)) {
+ return (SelectOperator) currentOp;
}
-
- if (nextOp.getInputs().isEmpty()) {
- break;
- }
- nextOp = nextOp.getInputs().get(0).getValue();
}
}
- op = op.getInputs().get(0).getValue();
+ currentOp = currentOp.getInputs().get(0).getValue();
}
return null;
}
- private List<ILogicalExpression> storeSubplanSelectsAndMakeThemTrue(ILogicalOperator op) {
+ // For the otherSelOp, leave the selection condition the same but all other selects and subplan selects should be marked true
+ private List<ILogicalExpression> storeSelectConditionsAndMakeThemTrue(ILogicalOperator op,
+ SelectOperator otherSelOp) {
List<ILogicalExpression> selExprs = new ArrayList<>();
- while (op != null && op.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
- if (op.getOperatorTag().equals(LogicalOperatorTag.SELECT)) {
- if (op.getInputs().get(0).getValue().getOperatorTag().equals(LogicalOperatorTag.SUBPLAN)) {
- SelectOperator selOp = (SelectOperator) op;
+ ILogicalOperator currentOp = op;
+ while (currentOp != null && currentOp.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
+ if (currentOp.getOperatorTag().equals(LogicalOperatorTag.SELECT)) {
+ SelectOperator selOp = (SelectOperator) currentOp;
+ if (selOp != otherSelOp) {
selExprs.add(selOp.getCondition().getValue());
selOp.getCondition().setValue(ConstantExpression.TRUE);
}
}
- op = op.getInputs().get(0).getValue();
+ currentOp = currentOp.getInputs().get(0).getValue();
}
return selExprs;
}
- private void restoreAllSubplanSelects(ILogicalOperator op, List<ILogicalExpression> selExprs) {
+ private void restoreAllSelectConditions(ILogicalOperator op, List<ILogicalExpression> selExprs,
+ ILogicalOperator otherSelOp) {
int i = 0;
- while (op != null && op.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
- if (op.getOperatorTag().equals(LogicalOperatorTag.SELECT)) {
- if (op.getInputs().get(0).getValue().getOperatorTag().equals(LogicalOperatorTag.SUBPLAN)) {
- SelectOperator selOp = (SelectOperator) op;
+ ILogicalOperator currentOp = op;
+ while (currentOp != null && currentOp.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
+ if (currentOp.getOperatorTag().equals(LogicalOperatorTag.SELECT)) {
+ SelectOperator selOp = (SelectOperator) currentOp;
+ if (selOp != otherSelOp) {
selOp.getCondition().setValue(selExprs.get(i));
i++;
}
}
- op = op.getInputs().get(0).getValue();
- }
- }
-
- // For the SubOp subplan, leave the selection condition the same but all other selects and subsplan selects should be marked true
- private List<ILogicalExpression> storeSubplanSelectsAndMakeThemTrue(ILogicalOperator op, SubplanOperator subOp) {
- List<ILogicalExpression> selExprs = new ArrayList<>();
- while (op != null && op.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
- if (op.getOperatorTag().equals(LogicalOperatorTag.SELECT)) {
- ILogicalOperator op2 = op.getInputs().get(0).getValue();
- if (op2.getOperatorTag().equals(LogicalOperatorTag.SUBPLAN)) {
- SubplanOperator subOp2 = (SubplanOperator) op2;
- if (subOp2 != subOp) {
- SelectOperator selOp = (SelectOperator) op;
- selExprs.add(selOp.getCondition().getValue());
- selOp.getCondition().setValue(ConstantExpression.TRUE);
- } // else leave expression as is.
- } else { // a non subplan select
- SelectOperator selOp = (SelectOperator) op;
- selExprs.add(selOp.getCondition().getValue());
- selOp.getCondition().setValue(ConstantExpression.TRUE);
- }
- }
- op = op.getInputs().get(0).getValue();
- }
- return selExprs;
- }
-
- private void restoreAllSubplanSelectConditions(ILogicalOperator op, List<ILogicalExpression> selExprs,
- SubplanOperator subOp) {
- int i = 0;
- while (op != null && op.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
- if (op.getOperatorTag().equals(LogicalOperatorTag.SELECT)) {
- ILogicalOperator op2 = op.getInputs().get(0).getValue();
- if (op2.getOperatorTag().equals(LogicalOperatorTag.SUBPLAN)) {
- SubplanOperator subOp2 = (SubplanOperator) op2;
- if (subOp2 != subOp) {
- SelectOperator selOp = (SelectOperator) op;
- selOp.getCondition().setValue(selExprs.get(i));
- i++;
- }
- } else { // a non subplan select
- SelectOperator selOp = (SelectOperator) op;
- selOp.getCondition().setValue(selExprs.get(i));
- }
- }
- op = op.getInputs().get(0).getValue();
+ currentOp = currentOp.getInputs().get(0).getValue();
}
}
@@ -465,56 +431,39 @@
List<ILogicalOperator> subPlans = countOps(selOp, LogicalOperatorTag.SUBPLAN);
int numSubplans = subPlans.size();
+ List<ILogicalOperator> selOps = countOps(selOp, LogicalOperatorTag.SELECT);
+ int numSelects = selOps.size();
+ int nonSubplanSelects = numSelects - numSubplans;
+
List<List<IAObject>> result;
-
- // insert this in place of the scandatasourceOp.
parent.getInputs().get(0).setValue(deepCopyofScan);
- if (numSubplans == 0) { // just switch the predicates; the simplest case. There should be no other case if subplans were canonical
- ILogicalExpression saveExprs = selOp.getCondition().getValue();
- selOp.getCondition().setValue(exp);
- result = runSamplingQuery(optCtx, selOp);
- selOp.getCondition().setValue(saveExprs);
- } else {
- List<ILogicalOperator> selOps = countOps(selOp, LogicalOperatorTag.SELECT);
- int numSelects = selOps.size();
- int nonSubplanSelects = numSelects - numSubplans;
-
- if (numSubplans == 1 && nonSubplanSelects == 0) {
- AggregateOperator aggOp = findAggOp(selOp, exp);
- if (aggOp.getExpressions().size() > 1) {
- // ANY and EVERY IN query; for selectivity purposes, we need to transform this into a ANY IN query
- SelectOperator newSelOp = (SelectOperator) OperatorManipulationUtil.bottomUpCopyOperators(selOp);
- aggOp = findAggOp(newSelOp, exp);
- ILogicalOperator input = aggOp.getInputs().get(0).getValue();
- SelectOperator condition = (SelectOperator) OperatorManipulationUtil
- .bottomUpCopyOperators(AbstractOperatorFromSubplanRewrite.getSelectFromPlan(aggOp));
- //push this condition below aggOp.
- aggOp.getInputs().get(0).setValue(condition);
- condition.getInputs().get(0).setValue(input);
- ILogicalExpression newExp2 = newSelOp.getCondition().getValue();
- if (newExp2.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) {
- AbstractFunctionCallExpression afce = (AbstractFunctionCallExpression) newExp2;
- afce.getArguments().get(1).setValue(ConstantExpression.TRUE);
- }
- result = runSamplingQuery(optCtx, newSelOp); // no need to switch anything
- } else
- result = runSamplingQuery(optCtx, selOp);
- } else { // the painful part; have to find where exp that is passed in is coming from. >= 1 and >= 1 case
- // Assumption is that there is exaclty one select condition above each subplan.
- // This was ensured before this routine is called
- SubplanOperator subOp = findSubplanWithExpr(selOp, exp);
- if (subOp == null) { // the exp is not coming from a subplan
- List<ILogicalExpression> selExprs;
- selExprs = storeSubplanSelectsAndMakeThemTrue(selOp); // all these will be marked true and will be resorted later.
- result = runSamplingQuery(optCtx, selOp);
- restoreAllSubplanSelects(selOp, selExprs);
- } else { // found the matching subPlan oper. Only keep this predicate and make all others true and then restore them.
- List<ILogicalExpression> selExprs;
- selExprs = storeSubplanSelectsAndMakeThemTrue(selOp, subOp); // all these will be marked true and will be resorted later.
- result = runSamplingQuery(optCtx, selOp);
- restoreAllSubplanSelectConditions(selOp, selExprs, subOp);
+ if (numSubplans == 1 && nonSubplanSelects == 0) {
+ AggregateOperator aggOp = findAggOp(selOp, exp);
+ if (aggOp.getExpressions().size() > 1) {
+ // ANY and EVERY IN query; for selectivity purposes, we need to transform this into a ANY IN query
+ SelectOperator newSelOp = (SelectOperator) OperatorManipulationUtil.bottomUpCopyOperators(selOp);
+ aggOp = findAggOp(newSelOp, exp);
+ ILogicalOperator input = aggOp.getInputs().get(0).getValue();
+ SelectOperator condition = (SelectOperator) OperatorManipulationUtil
+ .bottomUpCopyOperators(AbstractOperatorFromSubplanRewrite.getSelectFromPlan(aggOp));
+ //push this condition below aggOp.
+ aggOp.getInputs().get(0).setValue(condition);
+ condition.getInputs().get(0).setValue(input);
+ ILogicalExpression newExp2 = newSelOp.getCondition().getValue();
+ if (newExp2.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) {
+ AbstractFunctionCallExpression afce = (AbstractFunctionCallExpression) newExp2;
+ afce.getArguments().get(1).setValue(ConstantExpression.TRUE);
}
+ result = runSamplingQuery(optCtx, newSelOp); // no need to switch anything
+ } else {
+ result = runSamplingQuery(optCtx, selOp);
}
+ } else {
+ SelectOperator selOp2 = findSelectOpWithExpr(selOp, exp);
+ List<ILogicalExpression> selExprs;
+ selExprs = storeSelectConditionsAndMakeThemTrue(selOp, selOp2); // all these will be marked true and will be resorted later.
+ result = runSamplingQuery(optCtx, selOp);
+ restoreAllSelectConditions(selOp, selExprs, selOp2);
}
double predicateCardinality = findPredicateCardinality(result, false);
@@ -534,10 +483,12 @@
// SELECT count(*) as revenue
// FROM orders o, o.o_orderline ol
// WHERE TRUE;
- ILogicalExpression saveExprs = selOp.getCondition().getValue();
- selOp.getCondition().setValue(ConstantExpression.TRUE);
+
+ // Replace ALL SELECTS with TRUE
+ List<ILogicalExpression> selExprs;
+ selExprs = storeSelectConditionsAndMakeThemTrue(selOp, null); // all these will be marked true and will be resorted later.
result = runSamplingQuery(optCtx, selOp);
- selOp.getCondition().setValue(saveExprs);
+ restoreAllSelectConditions(selOp, selExprs, null);
sampleCard = findPredicateCardinality(result, false);
}
// switch the scanOp back
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/atomic-and-array-queries/query4.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/atomic-and-array-queries/query4.plan
new file mode 100644
index 0000000..6d57ef0
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/atomic-and-array-queries/query4.plan
@@ -0,0 +1,22 @@
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SUBPLAN |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- ASSIGN |LOCAL|
+ -- UNNEST |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- STREAM_SELECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (TestDataverse.Dataset1) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/atomic-and-array-queries/query5.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/atomic-and-array-queries/query5.plan
new file mode 100644
index 0000000..9f3e9a0
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/atomic-and-array-queries/query5.plan
@@ -0,0 +1,31 @@
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SUBPLAN |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- SUBPLAN |LOCAL|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- ASSIGN |LOCAL|
+ -- UNNEST |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- STREAM_SELECT |LOCAL|
+ -- ASSIGN |LOCAL|
+ -- UNNEST |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- STREAM_SELECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (TestDataverse.Dataset1) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-1/query2.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-1/query2.plan
new file mode 100644
index 0000000..1658a86
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-1/query2.plan
@@ -0,0 +1,23 @@
+-- DISTRIBUTE_RESULT |UNPARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ -- STREAM_PROJECT |UNPARTITIONED|
+ -- ASSIGN |UNPARTITIONED|
+ -- AGGREGATE |UNPARTITIONED|
+ -- RANDOM_MERGE_EXCHANGE |PARTITIONED|
+ -- AGGREGATE |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SUBPLAN |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- UNNEST |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (TestYelp.YelpCheckin) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-1/query4.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-1/query4.plan
new file mode 100644
index 0000000..2d561f4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-1/query4.plan
@@ -0,0 +1,22 @@
+-- DISTRIBUTE_RESULT |UNPARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ -- STREAM_PROJECT |UNPARTITIONED|
+ -- ASSIGN |UNPARTITIONED|
+ -- AGGREGATE |UNPARTITIONED|
+ -- RANDOM_MERGE_EXCHANGE |PARTITIONED|
+ -- AGGREGATE |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SUBPLAN |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- UNNEST |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (TestYelp.YelpCheckin) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-2/query2.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-2/query2.plan
new file mode 100644
index 0000000..1658a86
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-2/query2.plan
@@ -0,0 +1,23 @@
+-- DISTRIBUTE_RESULT |UNPARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ -- STREAM_PROJECT |UNPARTITIONED|
+ -- ASSIGN |UNPARTITIONED|
+ -- AGGREGATE |UNPARTITIONED|
+ -- RANDOM_MERGE_EXCHANGE |PARTITIONED|
+ -- AGGREGATE |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SUBPLAN |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- UNNEST |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (TestYelp.YelpCheckin) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-2/query3.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-2/query3.plan
new file mode 100644
index 0000000..2d561f4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-2/query3.plan
@@ -0,0 +1,22 @@
+-- DISTRIBUTE_RESULT |UNPARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ -- STREAM_PROJECT |UNPARTITIONED|
+ -- ASSIGN |UNPARTITIONED|
+ -- AGGREGATE |UNPARTITIONED|
+ -- RANDOM_MERGE_EXCHANGE |PARTITIONED|
+ -- AGGREGATE |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SUBPLAN |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- UNNEST |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (TestYelp.YelpCheckin) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-3/query3.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-3/query3.plan
new file mode 100644
index 0000000..7e503bb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/array-index/select-quantified-queries/use-case-3/query3.plan
@@ -0,0 +1,21 @@
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SUBPLAN |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- ASSIGN |LOCAL|
+ -- UNNEST |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (TestYelp.YelpCheckin) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.3.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.3.plan
index e24b70a..3028a95 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.3.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.3.plan
@@ -1,14 +1,14 @@
-distribute result [$$15] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+distribute result [$$15] [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- DISTRIBUTE_RESULT |UNPARTITIONED|
- exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- assign [$$15] <- [true] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$15] <- [true] [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- ASSIGN |UNPARTITIONED|
- project ([]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([]) [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- STREAM_PROJECT |UNPARTITIONED|
- exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- order (ASC, $$x) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ order (ASC, $$x) [cardinality: 0.0, op-cost: 33.22, total-cost: 33.22]
-- STABLE_SORT [$$x(ASC)] |UNPARTITIONED|
exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.5.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.5.plan
index 0d3b661..c5d8eb3 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.5.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.5.plan
@@ -1,14 +1,14 @@
-distribute result [$$15] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+distribute result [$$15] [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- DISTRIBUTE_RESULT |UNPARTITIONED|
- exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- project ([$$15]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$15]) [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- STREAM_PROJECT |UNPARTITIONED|
- assign [$$15] <- [le($$x, 2)] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$15] <- [le($$x, 2)] [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- ASSIGN |UNPARTITIONED|
- exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- order (ASC, $$x) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ order (ASC, $$x) [cardinality: 0.0, op-cost: 33.22, total-cost: 33.22]
-- STABLE_SORT [$$x(ASC)] |UNPARTITIONED|
exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.6.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.6.plan
index cc40968..3cb126f 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.6.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.6.plan
@@ -1,14 +1,14 @@
-distribute result [$$15] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+distribute result [$$15] [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- DISTRIBUTE_RESULT |UNPARTITIONED|
- exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- project ([$$15]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$15]) [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- STREAM_PROJECT |UNPARTITIONED|
- assign [$$15] <- [or(null, le($$x, 2))] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$15] <- [or(null, le($$x, 2))] [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- ASSIGN |UNPARTITIONED|
- exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- order (ASC, $$x) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ order (ASC, $$x) [cardinality: 0.0, op-cost: 33.22, total-cost: 33.22]
-- STABLE_SORT [$$x(ASC)] |UNPARTITIONED|
exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.7.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.7.plan
index 9ea5c09..c8c68c9 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.7.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/constant_folding/constant_folding.7.plan
@@ -1,14 +1,14 @@
-distribute result [$$17] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+distribute result [$$17] [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- DISTRIBUTE_RESULT |UNPARTITIONED|
- exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- assign [$$17] <- [true] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$17] <- [true] [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- ASSIGN |UNPARTITIONED|
- project ([]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([]) [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- STREAM_PROJECT |UNPARTITIONED|
- exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 33.22]
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- order (ASC, $$x) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ order (ASC, $$x) [cardinality: 0.0, op-cost: 33.22, total-cost: 33.22]
-- STABLE_SORT [$$x(ASC)] |UNPARTITIONED|
exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/filter/not-in_every/not-in_every.201.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/filter/not-in_every/not-in_every.201.plan
index b1b4bc2..1d6e680 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/filter/not-in_every/not-in_every.201.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/filter/not-in_every/not-in_every.201.plan
@@ -49,7 +49,7 @@
-- BROADCAST_EXCHANGE |PARTITIONED|
assign [$$23] <- [true] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |UNPARTITIONED|
- unnest $#1 <- scan-collection(array: [ "1", "2", "3" ]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $#1 <- scan-collection(array: [ "1", "2", "3" ]) [cardinality: 1.0, op-cost: 1.0, total-cost: 1.0]
-- UNNEST |UNPARTITIONED|
empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/pushdown/array-access-pushdown/array-access-pushdown.008.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/pushdown/array-access-pushdown/array-access-pushdown.008.plan
index 8631f06..491db48 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/pushdown/array-access-pushdown/array-access-pushdown.008.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/pushdown/array-access-pushdown/array-access-pushdown.008.plan
@@ -1,16 +1,16 @@
-distribute result [$$28] [cardinality: 2.0, op-cost: 0.0, total-cost: 4.0]
+distribute result [$$28] [cardinality: 2.0, op-cost: 0.0, total-cost: 2.0]
-- DISTRIBUTE_RESULT |PARTITIONED|
- exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 4.0]
+ exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 2.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$28]) [cardinality: 2.0, op-cost: 0.0, total-cost: 4.0]
+ project ([$$28]) [cardinality: 2.0, op-cost: 0.0, total-cost: 2.0]
-- STREAM_PROJECT |PARTITIONED|
- assign [$$28] <- [{"display_url": $$urls.getField("display_url")}] [cardinality: 2.0, op-cost: 0.0, total-cost: 4.0]
+ assign [$$28] <- [{"display_url": $$urls.getField("display_url")}] [cardinality: 2.0, op-cost: 0.0, total-cost: 2.0]
-- ASSIGN |PARTITIONED|
- project ([$$urls]) [cardinality: 2.0, op-cost: 0.0, total-cost: 4.0]
+ project ([$$urls]) [cardinality: 2.0, op-cost: 0.0, total-cost: 2.0]
-- STREAM_PROJECT |PARTITIONED|
- exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 4.0]
+ exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 2.0]
-- SORT_MERGE_EXCHANGE [$$30(ASC) ] |PARTITIONED|
- order (ASC, $$30) [cardinality: 2.0, op-cost: 2.0, total-cost: 4.0]
+ order (ASC, $$30) [cardinality: 2.0, op-cost: 0.0, total-cost: 2.0]
-- STABLE_SORT [$$30(ASC)] |PARTITIONED|
exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 2.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/pushdown/other-pushdowns/other-pushdowns.014.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/pushdown/other-pushdowns/other-pushdowns.014.plan
index d3f44d6..2182bcc 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/pushdown/other-pushdowns/other-pushdowns.014.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/column/pushdown/other-pushdowns/other-pushdowns.014.plan
@@ -93,7 +93,7 @@
-- ASSIGN |PARTITIONED|
project ([$$ht2]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
- unnest $$ht2 <- scan-collection($$108) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$ht2 <- scan-collection($$108) [cardinality: 1.0, op-cost: 2.0, total-cost: 2.0]
-- UNNEST |PARTITIONED|
project ([$$108]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|