[ASTERIXDB-3377][COMP]: CBO choosing wrong index
Change-Id: I81a6dcca634d3d30f0b101baabe4093fb01123d0
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/18236
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
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/cbo/JoinEnum.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java
index e419eea..951648a 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
@@ -30,6 +30,7 @@
import org.apache.asterix.common.annotations.IndexedNLJoinExpressionAnnotation;
import org.apache.asterix.common.annotations.SecondaryIndexSearchPreferenceAnnotation;
+import org.apache.asterix.common.annotations.SkipSecondaryIndexSearchExpressionAnnotation;
import org.apache.asterix.common.exceptions.AsterixException;
import org.apache.asterix.common.exceptions.ErrorCode;
import org.apache.asterix.metadata.declared.DataSource;
@@ -384,6 +385,29 @@
return false;
}
+ public SkipSecondaryIndexSearchExpressionAnnotation findSkipIndexHint(AbstractFunctionCallExpression condition) {
+ if (condition.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.AND)) {
+ for (int i = 0; i < condition.getArguments().size(); i++) {
+ ILogicalExpression expr = condition.getArguments().get(i).getValue();
+ if (expr.getExpressionTag().equals(LogicalExpressionTag.FUNCTION_CALL)) {
+ AbstractFunctionCallExpression AFCexpr = (AbstractFunctionCallExpression) expr;
+ SkipSecondaryIndexSearchExpressionAnnotation skipAnno =
+ AFCexpr.getAnnotation(SkipSecondaryIndexSearchExpressionAnnotation.class);
+ if (skipAnno != null) {
+ return skipAnno;
+ }
+ }
+ }
+ } else if (condition.getExpressionTag().equals(LogicalExpressionTag.FUNCTION_CALL)) {
+ SkipSecondaryIndexSearchExpressionAnnotation skipAnno =
+ condition.getAnnotation(SkipSecondaryIndexSearchExpressionAnnotation.class);
+ if (skipAnno != null) {
+ return skipAnno;
+ }
+ }
+ return null;
+ }
+
protected int findJoinNodeIndexByName(String name) {
for (int i = 1; i <= this.numberOfTerms; i++) {
if (name.equals(jnArray[i].datasetNames.get(0))) {
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 4543190..37212ba 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
@@ -21,8 +21,10 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -546,9 +548,20 @@
for (int i = 0; i < IndexCostInfo.size(); i++) {
if (IndexCostInfo.get(i).second == -1.0) {
AbstractFunctionCallExpression afce = IndexCostInfo.get(i).third;
- // this index has to be skipped, so find the corresponding expression
- EnumerateJoinsRule.setAnnotation(afce, SkipSecondaryIndexSearchExpressionAnnotation
- .newInstance(Collections.singleton(IndexCostInfo.get(i).first.getIndexName())));
+ SkipSecondaryIndexSearchExpressionAnnotation skipAnno = joinEnum.findSkipIndexHint(afce);
+ Collection<String> indexNames = new HashSet<>();
+ if (skipAnno != null && skipAnno.getIndexNames() != null) {
+ indexNames.addAll(skipAnno.getIndexNames());
+ }
+ if (indexNames.isEmpty()) {
+ // this index has to be skipped, so find the corresponding expression
+ EnumerateJoinsRule.setAnnotation(afce, SkipSecondaryIndexSearchExpressionAnnotation
+ .newInstance(Collections.singleton(IndexCostInfo.get(i).first.getIndexName())));
+ } else {
+ indexNames.add(IndexCostInfo.get(i).first.getIndexName());
+ EnumerateJoinsRule.setAnnotation(afce,
+ SkipSecondaryIndexSearchExpressionAnnotation.newInstance(indexNames));
+ }
}
}
}
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 254d364..785da69 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
@@ -446,33 +446,41 @@
List<List<IAObject>> result;
parent.getInputs().get(0).setValue(deepCopyofScan);
- 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.
+
+ if (numSelects == 1 && 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);
- restoreAllSelectConditions(selOp, selExprs, selOp2);
+ selOp.getCondition().setValue(saveExprs);
+ } else {
+ 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);
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/hints-use-index/hints-use-index-16.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/hints-use-index/hints-use-index-16.plan
index 00ba0d7..725e81e 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/hints-use-index/hints-use-index-16.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/hints-use-index/hints-use-index-16.plan
@@ -9,11 +9,11 @@
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
-- BTREE_SEARCH (test.tenk.tenk) |PARTITIONED|
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- -- STABLE_SORT [$$31(ASC)] |PARTITIONED|
+ -- STABLE_SORT [$$28(ASC)] |PARTITIONED|
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
-- STREAM_PROJECT |PARTITIONED|
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- -- BTREE_SEARCH (test.tenk.idx_1k_2k) |PARTITIONED|
+ -- BTREE_SEARCH (test.tenk.idx_2k) |PARTITIONED|
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
-- ASSIGN |PARTITIONED|
-- EMPTY_TUPLE_SOURCE |PARTITIONED|