You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by al...@apache.org on 2026/10/01 19:33:15 UTC
(asterixdb) branch master updated: [ASTERIXDB-3896][ASTERIXDB-3894][COMP] Treat an IN list as one optimizable expression
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git
The following commit(s) were added to refs/heads/master by this push:
new 6764af989f [ASTERIXDB-3896][ASTERIXDB-3894][COMP] Treat an IN list as one optimizable expression
6764af989f is described below
commit 6764af989f772d367150f72cc97e275d1966d710
Author: Ali Alsuliman <al...@gmail.com>
AuthorDate: Thu Sep 24 15:57:01 2026 -0700
[ASTERIXDB-3896][ASTERIXDB-3894][COMP] Treat an IN list as one optimizable expression
- user model changes: no
- storage format changes: no
- interface changes: no
Details: an IN list of constants, translated to an OR of equalities,
was analyzed as one optimizable expression per disjunct. The search
keys were deduplicated only as structurally equal equalities, so a key
reaching the B-tree search twice (a = 1 OR 1 = a, or two IN lists that
share a key) returned each of its records twice. The same form made
compile time quadratic in the length of the list, since every consumer
of the matched expressions walked all of them and several compared
each against the rest.
The disjunction is now a single DisjunctiveEqualityFuncExpr holding
its distinct constants, and each key position's search keys are
deduplicated by value. Only an index with such an expression takes the
OR search plan, so the other indexes of an intersection keep their
exact bounds and no longer retain their input.
Ext-ref: MB-72987
Generated-by: Claude Opus 5.5 <no...@anthropic.com>
Change-Id: Ibd0a0e3472dd710d60afc61cbdad26e941bb2ecd
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/21886
Reviewed-by: Ali Alsuliman <al...@gmail.com>
Reviewed-by: Murtadha Hubail <mh...@apache.org>
Tested-by: Ali Alsuliman <al...@gmail.com>
---
.../am/AbstractIntroduceAccessMethodRule.java | 61 +++++++++-
.../optimizer/rules/am/AccessMethodUtils.java | 15 ++-
.../optimizer/rules/am/BTreeAccessMethod.java | 134 ++++++++++-----------
.../rules/am/DisjunctiveEqualityFuncExpr.java | 49 ++++++++
.../rules/am/IntroduceSelectAccessMethodRule.java | 12 +-
.../asterix/optimizer/rules/cbo/JoinNode.java | 12 +-
.../test/optimizer/InListCompilationTimeTest.java | 134 +++++++++++++++++++++
.../disjunctive-predicate-4.plan | 40 +++---
.../disjunctive-predicate-5.plan | 40 +++---
.../disjunctive-predicate-7.plan | 40 +++---
.../in-list-duplicate-keys.1.ddl.sqlpp | 31 +++++
.../in-list-duplicate-keys.10.query.sqlpp | 23 ++++
.../in-list-duplicate-keys.11.query.sqlpp | 23 ++++
.../in-list-duplicate-keys.12.query.sqlpp | 22 ++++
.../in-list-duplicate-keys.13.query.sqlpp | 23 ++++
.../in-list-duplicate-keys.14.query.sqlpp | 22 ++++
.../in-list-duplicate-keys.15.query.sqlpp | 23 ++++
.../in-list-duplicate-keys.16.query.sqlpp | 22 ++++
.../in-list-duplicate-keys.17.query.sqlpp | 22 ++++
.../in-list-duplicate-keys.18.query.sqlpp | 23 ++++
.../in-list-duplicate-keys.19.query.sqlpp | 23 ++++
.../in-list-duplicate-keys.2.update.sqlpp | 37 ++++++
.../in-list-duplicate-keys.20.query.sqlpp | 22 ++++
.../in-list-duplicate-keys.21.query.sqlpp | 22 ++++
.../in-list-duplicate-keys.22.query.sqlpp | 23 ++++
.../in-list-duplicate-keys.23.query.sqlpp | 22 ++++
.../in-list-duplicate-keys.3.query.sqlpp | 23 ++++
.../in-list-duplicate-keys.4.query.sqlpp | 23 ++++
.../in-list-duplicate-keys.5.query.sqlpp | 23 ++++
.../in-list-duplicate-keys.6.query.sqlpp | 22 ++++
.../in-list-duplicate-keys.7.query.sqlpp | 23 ++++
.../in-list-duplicate-keys.8.query.sqlpp | 22 ++++
.../in-list-duplicate-keys.9.query.sqlpp | 23 ++++
.../btree-index-range-02.16.plan | 34 +++---
.../btree-index-range-02.18.plan | 24 ++--
.../in-list-duplicate-keys.10.adm | 1 +
.../in-list-duplicate-keys.11.adm | 3 +
.../in-list-duplicate-keys.12.adm | 2 +
.../in-list-duplicate-keys.13.adm | 1 +
.../in-list-duplicate-keys.14.adm | 1 +
.../in-list-duplicate-keys.15.adm | 3 +
.../in-list-duplicate-keys.16.adm | 1 +
.../in-list-duplicate-keys.17.adm | 2 +
.../in-list-duplicate-keys.18.adm | 1 +
.../in-list-duplicate-keys.19.adm | 1 +
.../in-list-duplicate-keys.20.adm | 1 +
.../in-list-duplicate-keys.21.adm | 2 +
.../in-list-duplicate-keys.22.adm | 2 +
.../in-list-duplicate-keys.23.adm | 2 +
.../in-list-duplicate-keys.3.adm | 4 +
.../in-list-duplicate-keys.4.adm | 4 +
.../in-list-duplicate-keys.5.adm | 2 +
.../in-list-duplicate-keys.6.adm | 3 +
.../in-list-duplicate-keys.7.adm | 2 +
.../in-list-duplicate-keys.8.adm | 4 +
.../in-list-duplicate-keys.9.adm | 3 +
.../secondary-index-intersection-01.12.plan | 26 ++--
.../secondary-index-intersection-01.18.plan | 36 +++---
.../secondary-index-intersection-01.20.plan | 36 +++---
.../secondary-index-or-in-01.16.plan | 2 +-
.../btree-index-range-02.18.plan | 30 +++--
.../btree-index-range-02.16.plan | 34 +++---
.../btree-index-range-02.18.plan | 24 ++--
.../secondary-index-intersection-01.12.plan | 26 ++--
.../secondary-index-intersection-01.18.plan | 36 +++---
.../secondary-index-intersection-01.20.plan | 36 +++---
.../secondary-index-or-in-01.16.plan | 2 +-
.../src/test/resources/runtimets/sqlpp_queries.xml | 5 +
68 files changed, 1150 insertions(+), 325 deletions(-)
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 79c71cc117..9f8e23457d 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
@@ -21,11 +21,13 @@ package org.apache.asterix.optimizer.rules.am;
import static org.apache.asterix.om.types.AOrderedListType.FULL_OPEN_ORDEREDLIST_TYPE;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -799,12 +801,11 @@ public abstract class AbstractIntroduceAccessMethodRule implements IAlgebraicRew
found = false;
break;
}
+ }
- found = analyzeSelectOrJoinOpConditionAndUpdateAnalyzedAM(argFuncExpr, assignsAndUnnests,
- disjuncAnalyzedAMs, context, typeEnvironment);
- if (!found) {
- break;
- }
+ if (found) {
+ found = analyzeEqualityDisjunction(funcExpr, assignsAndUnnests, disjuncAnalyzedAMs, context,
+ typeEnvironment);
}
if (found) {
@@ -869,6 +870,56 @@ public abstract class AbstractIntroduceAccessMethodRule implements IAlgebraicRew
return found;
}
+ /**
+ * Analyzes a disjunction of equalities between one variable and constants of one type, e.g. an IN list, as a
+ * single {@link DisjunctiveEqualityFuncExpr}. An access method's analysis of such an equality does not depend on
+ * the constant, so only the first disjunct is analyzed. One optimizable expression per disjunct would make every
+ * consumer of the matched expressions pay per disjunct, and several of them did so quadratically.
+ */
+ private boolean analyzeEqualityDisjunction(AbstractFunctionCallExpression disjunction,
+ List<AbstractLogicalOperator> assignsAndUnnests,
+ Map<IAccessMethod, AccessMethodAnalysisContext> disjuncAnalyzedAMs, IOptimizationContext context,
+ IVariableTypeEnvironment typeEnvironment) throws AlgebricksException {
+ List<Mutable<ILogicalExpression>> disjuncts = disjunction.getArguments();
+ AbstractFunctionCallExpression first = (AbstractFunctionCallExpression) disjuncts.get(0).getValue();
+ if (!analyzeSelectOrJoinOpConditionAndUpdateAnalyzedAM(first, assignsAndUnnests, disjuncAnalyzedAMs, context,
+ typeEnvironment)) {
+ return false;
+ }
+ for (AccessMethodAnalysisContext analysisCtx : disjuncAnalyzedAMs.values()) {
+ List<IOptimizableFuncExpr> matched = analysisCtx.getMatchedFuncExprs();
+ // Each access method that matches an equality of a variable and a constant adds one expression for it.
+ if (matched.size() != 1 || matched.get(0).getFuncExpr() != first) {
+ return false;
+ }
+ }
+
+ // Distinct values, not distinct disjuncts: a = 1 and 1 = a are the same key, and a key listed twice is
+ // searched for twice and returns each of its records twice.
+ Set<ILogicalExpression> constants = new LinkedHashSet<>();
+ for (Mutable<ILogicalExpression> disjunct : disjuncts) {
+ constants.add(constantArg((AbstractFunctionCallExpression) disjunct.getValue()));
+ }
+ ILogicalExpression[] constantExprs = constants.toArray(new ILogicalExpression[0]);
+ for (AccessMethodAnalysisContext analysisCtx : disjuncAnalyzedAMs.values()) {
+ IOptimizableFuncExpr firstMatch = analysisCtx.getMatchedFuncExpr(0);
+ // The caller only accepts constants of one type.
+ IAType[] constantTypes = new IAType[constantExprs.length];
+ Arrays.fill(constantTypes, firstMatch.getConstantType(0));
+ analysisCtx.getMatchedFuncExprs().set(0, new DisjunctiveEqualityFuncExpr(disjunction, first,
+ firstMatch.getLogicalVar(0), variableArgIndex(first), constantExprs.clone(), constantTypes));
+ }
+ return true;
+ }
+
+ private static int variableArgIndex(AbstractFunctionCallExpression eqExpr) {
+ return eqExpr.getArguments().get(0).getValue().getExpressionTag() == LogicalExpressionTag.VARIABLE ? 0 : 1;
+ }
+
+ private static ILogicalExpression constantArg(AbstractFunctionCallExpression eqExpr) {
+ return eqExpr.getArguments().get(1 - variableArgIndex(eqExpr)).getValue();
+ }
+
/**
* Analyzes the given selection condition, filling analyzedAMs with
* applicable access method types. At this point we are not yet consulting
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
index 2711a965d3..af4d50ddad 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
@@ -612,6 +612,17 @@ public class AccessMethodUtils {
public static Triple<ILogicalExpression, ILogicalExpression, Boolean> createSearchKeyExpr(Index index,
IOptimizableFuncExpr optFuncExpr, IAType indexedFieldType, OptimizableOperatorSubTree probeSubTree,
SearchKeyRoundingFunctionProvider roundingFunctionProvider) throws AlgebricksException {
+ return createSearchKeyExpr(index, optFuncExpr, 0, indexedFieldType, probeSubTree, roundingFunctionProvider);
+ }
+
+ /**
+ * As {@link #createSearchKeyExpr(Index, IOptimizableFuncExpr, IAType, OptimizableOperatorSubTree,
+ * SearchKeyRoundingFunctionProvider)}, but from the given constant of the optimizable function expression.
+ */
+ public static Triple<ILogicalExpression, ILogicalExpression, Boolean> createSearchKeyExpr(Index index,
+ IOptimizableFuncExpr optFuncExpr, int constantIndex, IAType indexedFieldType,
+ OptimizableOperatorSubTree probeSubTree, SearchKeyRoundingFunctionProvider roundingFunctionProvider)
+ throws AlgebricksException {
SourceLocation sourceLoc = optFuncExpr.getFuncExpr().getSourceLocation();
if (probeSubTree == null) {
// We are optimizing a selection query. Search key is a constant.
@@ -626,13 +637,13 @@ public class AccessMethodUtils {
return new Triple<>(varRef, null, false);
}
- ILogicalExpression constantAtRuntimeExpression = optFuncExpr.getConstantExpr(0);
+ ILogicalExpression constantAtRuntimeExpression = optFuncExpr.getConstantExpr(constantIndex);
AsterixConstantValue constantValue = null;
if (constantAtRuntimeExpression.getExpressionTag() == LogicalExpressionTag.CONSTANT) {
constantValue = (AsterixConstantValue) ((ConstantExpression) constantAtRuntimeExpression).getValue();
}
- ATypeTag constantValueTag = optFuncExpr.getConstantType(0).getTypeTag();
+ ATypeTag constantValueTag = optFuncExpr.getConstantType(constantIndex).getTypeTag();
ATypeTag indexedFieldTypeTag = TypeComputeUtils.getActualType(indexedFieldType).getTypeTag();
// type casting happened from real (FLOAT, DOUBLE) value -> INT value?
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
index bad4643c0a..671c74ba9c 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
@@ -31,6 +31,7 @@ import java.util.BitSet;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -269,69 +270,39 @@ public class BTreeAccessMethod implements IAccessMethod {
return true;
}
+ /**
+ * Replaces each disjunction of equalities that the index search turned into a list of search keys with a test
+ * that the record equals the key that found it, which the search keeps alongside the record.
+ */
public static void optimizeSelectCondition(Mutable<ILogicalExpression> cond,
List<Pair<LogicalVariable, List<ILogicalExpression>>> optimizableDisjunctionConditions) {
- if (cond.getValue().getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) {
- AbstractFunctionCallExpression funcExpr = (AbstractFunctionCallExpression) cond.getValue();
- if (funcExpr.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.OR)) {
-
- LogicalVariable variable = null;
- Set<ConstantExpression> constantExpressions = new HashSet<>();
- List<ILogicalExpression> orExprs = new ArrayList<>();
-
- for (Mutable<ILogicalExpression> arg : funcExpr.getArguments()) {
- if (arg.get().getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
- return;
- }
-
- AbstractFunctionCallExpression argFuncExpr = (AbstractFunctionCallExpression) arg.get();
-
- if (!argFuncExpr.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.EQ)) {
- return;
- }
-
- ILogicalExpression arg1 = argFuncExpr.getArguments().get(0).get();
- ILogicalExpression arg2 = argFuncExpr.getArguments().get(1).get();
-
- if (arg1.getExpressionTag() == LogicalExpressionTag.VARIABLE
- && arg2.getExpressionTag() == LogicalExpressionTag.CONSTANT) {
- if (variable == null) {
- variable = ((VariableReferenceExpression) arg1).getVariableReference();
- } else if (!variable.equals(((VariableReferenceExpression) arg1).getVariableReference())) {
- return;
- }
-
- constantExpressions.add((ConstantExpression) arg2);
- } else if (arg2.getExpressionTag() == LogicalExpressionTag.VARIABLE
- && arg1.getExpressionTag() == LogicalExpressionTag.CONSTANT) {
- if (variable == null) {
- variable = ((VariableReferenceExpression) arg2).getVariableReference();
- } else if (!variable.equals(((VariableReferenceExpression) arg2).getVariableReference())) {
- return;
- }
- constantExpressions.add((ConstantExpression) arg1);
- }
- orExprs.add(arg.get());
- }
-
- for (Pair<LogicalVariable, List<ILogicalExpression>> optimizableDisjunctionCondition : optimizableDisjunctionConditions) {
- if (optimizableDisjunctionCondition.getRight().containsAll(orExprs)
- && optimizableDisjunctionCondition.getRight().size() == orExprs.size()) {
- List<Mutable<ILogicalExpression>> args = new ArrayList<>();
- args.add(new MutableObject<>(new VariableReferenceExpression(variable)));
- args.add(new MutableObject<>(
- new VariableReferenceExpression(optimizableDisjunctionCondition.getLeft())));
- cond.setValue(
- new ScalarFunctionCallExpression(BuiltinFunctions.getBuiltinFunctionInfo(EQ), args));
- break;
- }
- }
-
- } else if (funcExpr.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.AND)) {
- for (Mutable<ILogicalExpression> arg : funcExpr.getArguments()) {
- optimizeSelectCondition(arg, optimizableDisjunctionConditions);
+ if (cond.getValue().getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
+ return;
+ }
+ AbstractFunctionCallExpression funcExpr = (AbstractFunctionCallExpression) cond.getValue();
+ if (funcExpr.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.OR)) {
+ for (Pair<LogicalVariable, List<ILogicalExpression>> keyVarAndConditions : optimizableDisjunctionConditions) {
+ // The keys must be this disjunction's constants and nothing else's. Compared structurally, since the
+ // condition may be a copy of the one the keys came from.
+ List<ILogicalExpression> conditions = keyVarAndConditions.getRight();
+ if (conditions.size() == 1 && (conditions.get(0) == funcExpr || conditions.get(0).equals(funcExpr))) {
+ AbstractFunctionCallExpression firstDisjunct =
+ (AbstractFunctionCallExpression) funcExpr.getArguments().get(0).getValue();
+ ILogicalExpression variable = firstDisjunct.getArguments().get(0).getValue()
+ .getExpressionTag() == LogicalExpressionTag.VARIABLE
+ ? firstDisjunct.getArguments().get(0).getValue()
+ : firstDisjunct.getArguments().get(1).getValue();
+ List<Mutable<ILogicalExpression>> args = new ArrayList<>();
+ args.add(new MutableObject<>(variable.cloneExpression()));
+ args.add(new MutableObject<>(new VariableReferenceExpression(keyVarAndConditions.getLeft())));
+ cond.setValue(new ScalarFunctionCallExpression(BuiltinFunctions.getBuiltinFunctionInfo(EQ), args));
+ return;
}
}
+ } else if (funcExpr.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.AND)) {
+ for (Mutable<ILogicalExpression> arg : funcExpr.getArguments()) {
+ optimizeSelectCondition(arg, optimizableDisjunctionConditions);
+ }
}
}
@@ -417,17 +388,11 @@ public class BTreeAccessMethod implements IAccessMethod {
chosenIndexKeyFieldTypes, chosenIndexKeyFieldSourceIndicators, optimizableDisjunctionConditions);
}
- private static boolean containsOR(ILogicalExpression condition) {
- if (condition.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) {
- AbstractFunctionCallExpression funcExpr = (AbstractFunctionCallExpression) condition;
- if (funcExpr.getFunctionIdentifier().equals(AlgebricksBuiltinFunctions.OR)) {
+ private static boolean hasDisjunctiveEquality(AccessMethodAnalysisContext analysisCtx, Index chosenIndex) {
+ for (Pair<Integer, Integer> exprAndVar : analysisCtx.getIndexExprsFromIndexExprsAndVars(chosenIndex)) {
+ if (analysisCtx.getMatchedFuncExpr(exprAndVar.getLeft()) instanceof DisjunctiveEqualityFuncExpr) {
return true;
}
- for (Mutable<ILogicalExpression> arg : funcExpr.getArguments()) {
- if (containsOR(arg.get())) {
- return true;
- }
- }
}
return false;
}
@@ -435,7 +400,7 @@ public class BTreeAccessMethod implements IAccessMethod {
private void createKeyVarsAndExprsWithOr(int numKeys, LimitType[] keyLimits, ILogicalExpression[] searchKeyExprs,
ArrayList<LogicalVariable> assignKeyVarList, ArrayList<List<Mutable<ILogicalExpression>>> assignKeyExprList,
ArrayList<LogicalVariable> keyVarList, IOptimizationContext context, ILogicalExpression[] constExpressions,
- LogicalVariable[] constExprVars, List<ILogicalExpression>[] disjunctiveEqualityKeyExprs,
+ LogicalVariable[] constExprVars, Set<ILogicalExpression>[] disjunctiveEqualityKeyExprs,
List<ILogicalExpression>[] disjunctiveEqualityConditionExprs, boolean[] isDisjunctiveEqualityCondition,
boolean isHighKey, int numlowKeys, List<List<ILogicalExpression>> disjunctiveEqualityConditionExprSet) {
for (int i = 0; i < numKeys; i++) {
@@ -510,7 +475,7 @@ public class BTreeAccessMethod implements IAccessMethod {
LogicalVariable[] lowKeyConstAtRuntimeExprVars = new LogicalVariable[numSecondaryKeys];
LogicalVariable[] highKeyConstAtRuntimeExprVars = new LogicalVariable[numSecondaryKeys];
boolean[] isDisjunctiveEqualityCondition = new boolean[numSecondaryKeys];
- List<ILogicalExpression>[] disjunctiveEqualityKeyExprs = new List[numSecondaryKeys];
+ Set<ILogicalExpression>[] disjunctiveEqualityKeyExprs = new Set[numSecondaryKeys];
List<ILogicalExpression>[] disjunctiveEqualityConditionExprs = new List[numSecondaryKeys];
LogicalVariable[] disjunctiveRuntimeExprVars = new LogicalVariable[numSecondaryKeys];
@@ -541,6 +506,30 @@ public class BTreeAccessMethod implements IAccessMethod {
// with a FLOAT or a DOUBLE constant that will be fed into an INTEGER index.
// This is required because of type-casting. Refer to AccessMethodUtils.createSearchKeyExpr for details.
IAType indexedFieldType = chosenIndexKeyFieldTypes.get(keyPos);
+ if (optFuncExpr instanceof DisjunctiveEqualityFuncExpr && probeSubTree == null) {
+ if (!isDisjunctiveEqualityCondition[keyPos]) {
+ isDisjunctiveEqualityCondition[keyPos] = true;
+ disjunctiveEqualityKeyExprs[keyPos] = new LinkedHashSet<>();
+ disjunctiveEqualityConditionExprs[keyPos] = new ArrayList<>();
+ }
+ for (int i = 0; i < optFuncExpr.getNumConstantExpr(); i++) {
+ Triple<ILogicalExpression, ILogicalExpression, Boolean> searchKey =
+ AccessMethodUtils.createSearchKeyExpr(chosenIndex, optFuncExpr, i, indexedFieldType,
+ probeSubTree, SEARCH_KEY_ROUNDING_FUNCTION_COMPUTER);
+ // A second key is a real constant on an integer index, which a single equality gives up on too.
+ if (searchKey == null || searchKey.second != null
+ || searchKey.first.getExpressionTag() != LogicalExpressionTag.CONSTANT) {
+ return null;
+ }
+ disjunctiveEqualityKeyExprs[keyPos].add(searchKey.first);
+ }
+ AbstractFunctionCallExpression disjunction =
+ ((DisjunctiveEqualityFuncExpr) optFuncExpr).getDisjunction();
+ disjunctiveEqualityConditionExprs[keyPos].add(disjunction);
+ replacedFuncExprs.add(disjunction);
+ isEqCondition = true;
+ continue;
+ }
Triple<ILogicalExpression, ILogicalExpression, Boolean> returnedSearchKeyExpr =
AccessMethodUtils.createSearchKeyExpr(chosenIndex, optFuncExpr, indexedFieldType, probeSubTree,
SEARCH_KEY_ROUNDING_FUNCTION_COMPUTER);
@@ -627,7 +616,7 @@ public class BTreeAccessMethod implements IAccessMethod {
} else {
isDisjunctiveEqualityCondition[keyPos] = true;
isEqCondition = true;
- disjunctiveEqualityKeyExprs[keyPos] = new ArrayList<>();
+ disjunctiveEqualityKeyExprs[keyPos] = new LinkedHashSet<>();
disjunctiveEqualityKeyExprs[keyPos].add(searchKeyExpr);
disjunctiveEqualityConditionExprs[keyPos] = new ArrayList<>();
disjunctiveEqualityConditionExprs[keyPos].add(optFuncExpr.getFuncExpr());
@@ -1024,7 +1013,8 @@ public class BTreeAccessMethod implements IAccessMethod {
List<Pair<LogicalVariable, List<ILogicalExpression>>> optimizableDisjunctionConditions)
throws AlgebricksException {
- if (containsOR(conditionRef.get())) {
+ // A disjunction of equalities stands for all its keys, which only the OR plan searches for.
+ if (hasDisjunctiveEquality(analysisCtx, chosenIndex)) {
return createBTreeIndexSearchPlanWithOr(afterTopOpRefs, topOpRef, conditionRef, assignBeforeTheOpRefs,
indexSubTree, probeSubTree, chosenIndex, analysisCtx, retainInput, retainMissing, requiresBroadcast,
context, newMissingNullPlaceHolderForLOJ, leftOuterMissingValue, chosenIndexKeyFieldNames,
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/DisjunctiveEqualityFuncExpr.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/DisjunctiveEqualityFuncExpr.java
new file mode 100644
index 0000000000..345b178327
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/DisjunctiveEqualityFuncExpr.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.optimizer.rules.am;
+
+import org.apache.asterix.om.types.IAType;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+
+/**
+ * A disjunction of equalities between one variable and constants of one type, e.g. an IN list, as a single
+ * optimizable expression rather than one per disjunct.
+ * <p>
+ * {@link #getFuncExpr()} is the first disjunct, so that callers which inspect the comparison or its annotations see
+ * an equality; {@link #getDisjunction()} is the whole disjunction. The constants are those of the disjuncts with
+ * duplicates removed, the first disjunct's constant at index 0.
+ */
+public class DisjunctiveEqualityFuncExpr extends OptimizableFuncExpr {
+
+ private final AbstractFunctionCallExpression disjunction;
+
+ public DisjunctiveEqualityFuncExpr(AbstractFunctionCallExpression disjunction,
+ AbstractFunctionCallExpression firstDisjunct, LogicalVariable logicalVar, int varIndexInFirstDisjunct,
+ ILogicalExpression[] constantExpressions, IAType[] constantExpressionTypes) {
+ super(firstDisjunct, new LogicalVariable[] { logicalVar }, new int[] { varIndexInFirstDisjunct },
+ constantExpressions, constantExpressionTypes);
+ this.disjunction = disjunction;
+ }
+
+ public AbstractFunctionCallExpression getDisjunction() {
+ return disjunction;
+ }
+}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java
index 03accac7f2..f0096301a2 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java
@@ -679,13 +679,18 @@ public class IntroduceSelectAccessMethodRule extends AbstractIntroduceAccessMeth
List<IOptimizableFuncExpr> funcExprs =
analyzedAMs.get(indexAccessInfo.getAccessMethod()).getMatchedFuncExprs();
IndexInfo indexInfo = new IndexInfo();
- indexInfo.indexExprs = new ArrayList<>(Collections.nCopies(funcExprs.size(), null));
+ // Sized for every key position, which one expression per predicate (an IN list is one) may not reach.
+ indexInfo.indexExprs =
+ new ArrayList<>(Collections.nCopies(Math.max(funcExprs.size(), indexFields.size()), null));
+ indexInfo.numFuncExprs = funcExprs.size();
indexInfo.numKeys = indexFields.size();
indexInfo.indexaccessinfo = indexAccessInfo;
for (IOptimizableFuncExpr expr : funcExprs) {
for (int i = 0; i < expr.getNumLogicalVars(); i++) {
int i1 = indexFields.indexOf(expr.getFieldName(i));
- indexInfo.indexExprs.set(i1, expr.getFuncExpr().getFunctionIdentifier());
+ if (i1 >= 0) {
+ indexInfo.indexExprs.set(i1, expr.getFuncExpr().getFunctionIdentifier());
+ }
}
}
indexesWithMaxMatchedKeysInfo.add(indexInfo);
@@ -695,6 +700,7 @@ public class IntroduceSelectAccessMethodRule extends AbstractIntroduceAccessMeth
class IndexInfo implements Comparable<IndexInfo> {
List<FunctionIdentifier> indexExprs = new ArrayList<>();
+ int numFuncExprs;
int numKeys;
IndexAccessInfo indexaccessinfo;
@@ -710,7 +716,7 @@ public class IntroduceSelectAccessMethodRule extends AbstractIntroduceAccessMeth
}
}
- if (this.indexExprs.size() != other.indexExprs.size()) {
+ if (this.numFuncExprs != other.numFuncExprs) {
return 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 3a704ddfdd..0fa37acff7 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
@@ -48,6 +48,7 @@ import org.apache.asterix.optimizer.cost.Cost;
import org.apache.asterix.optimizer.cost.ICost;
import org.apache.asterix.optimizer.rules.am.AbstractIntroduceAccessMethodRule;
import org.apache.asterix.optimizer.rules.am.AccessMethodAnalysisContext;
+import org.apache.asterix.optimizer.rules.am.DisjunctiveEqualityFuncExpr;
import org.apache.asterix.optimizer.rules.am.IAccessMethod;
import org.apache.asterix.optimizer.rules.am.IOptimizableFuncExpr;
import org.apache.asterix.optimizer.rules.am.IntroduceJoinAccessMethodRule;
@@ -636,12 +637,17 @@ public class JoinNode {
return DummyPlanNode.INSTANCE;
}
+ private static AbstractFunctionCallExpression predicateOf(IOptimizableFuncExpr optFuncExpr) {
+ return optFuncExpr instanceof DisjunctiveEqualityFuncExpr
+ ? ((DisjunctiveEqualityFuncExpr) optFuncExpr).getDisjunction() : optFuncExpr.getFuncExpr();
+ }
+
private AbstractFunctionCallExpression buildExpr(List<IOptimizableFuncExpr> exprs,
List<Pair<Integer, Integer>> pairs) {
int i;
if (pairs.size() == 1) {
i = pairs.get(0).getLeft();
- return exprs.get(i).getFuncExpr();
+ return predicateOf(exprs.get(i));
}
ScalarFunctionCallExpression andExpr = new ScalarFunctionCallExpression(
@@ -651,7 +657,7 @@ public class JoinNode {
new TreeMap<>(Comparator.comparingInt(LogicalVariable::getId));
for (i = 0; i < pairs.size(); i++) {
IOptimizableFuncExpr funcExpr = exprs.get(pairs.get(i).getLeft());
- ILogicalExpression expr = funcExpr.getFuncExpr();
+ ILogicalExpression expr = predicateOf(funcExpr);
if (expr.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) {
AbstractFunctionCallExpression afce = (AbstractFunctionCallExpression) expr;
@@ -663,7 +669,7 @@ public class JoinNode {
}
}
} else {
- andExpr.getArguments().add(new MutableObject<>(funcExpr.getFuncExpr()));
+ andExpr.getArguments().add(new MutableObject<>(expr));
}
}
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/optimizer/InListCompilationTimeTest.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/optimizer/InListCompilationTimeTest.java
new file mode 100644
index 0000000000..42c2fece83
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/optimizer/InListCompilationTimeTest.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.test.optimizer;
+
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.LongStream;
+
+import org.apache.asterix.api.common.AsterixHyracksIntegrationUtil;
+import org.apache.asterix.api.java.AsterixJavaClient;
+import org.apache.asterix.app.translator.DefaultStatementExecutorFactory;
+import org.apache.asterix.common.config.GlobalConfig;
+import org.apache.asterix.common.dataflow.ICcApplicationContext;
+import org.apache.asterix.common.metadata.NamespaceResolver;
+import org.apache.asterix.compiler.provider.SqlppCompilationProvider;
+import org.apache.asterix.file.StorageComponentProvider;
+import org.apache.asterix.translator.SessionConfig;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Compiles queries with a long IN list of constants and bounds the time each takes. Handled in time linear in the
+ * length of the list, one compiles in about a second; the index-selection rules have handled such a list in time
+ * quadratic in its length, which takes minutes. The bound sits far from both, so it detects the growth rate rather
+ * than the speed of the machine.
+ */
+@RunWith(Parameterized.class)
+public class InListCompilationTimeTest {
+
+ private static final String CONFIG_FILE = "src/main/resources/cc_no_cbo.conf";
+ private static final int LIST_LENGTH = 30_000;
+ private static final long MAX_COMPILE_SECONDS = 15;
+
+ private static final String DDL = "DROP DATAVERSE test IF EXISTS; CREATE DATAVERSE test; USE test;"
+ + " CREATE TYPE T AS { id: bigint };" + " CREATE DATASET ds(T) PRIMARY KEY id;"
+ + " CREATE INDEX idx_a ON ds(a: bigint);" + " CREATE INDEX idx_ca ON ds(c: bigint, b: bigint);";
+
+ private static final AsterixHyracksIntegrationUtil integrationUtil = new AsterixHyracksIntegrationUtil();
+
+ private final String query;
+ private final boolean cbo;
+ private final String expectedIndex;
+
+ public InListCompilationTimeTest(String name, String query, boolean cbo, String expectedIndex) {
+ this.query = query;
+ this.cbo = cbo;
+ this.expectedIndex = expectedIndex;
+ }
+
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> shapes() {
+ String list = list(1);
+ Object[][] shapes = { { "secondary index", "WHERE d.a IN " + list, "idx_a" },
+ { "no index", "WHERE d.b IN " + list, null }, { "primary index", "WHERE d.id IN " + list, "ds" },
+ { "composite index", "WHERE d.c = 1 AND d.b IN " + list, "idx_ca" }, { "two IN lists on one key",
+ "WHERE d.a IN " + list + " AND d.a IN " + list(LIST_LENGTH / 2), "idx_a" } };
+ List<Object[]> tests = new ArrayList<>();
+ for (Object[] shape : shapes) {
+ String query = "SELECT VALUE d.id FROM ds d " + shape[1] + ";";
+ tests.add(new Object[] { shape[0] + ", no CBO", query, false, shape[2] });
+ // Without statistics, CBO may prefer a scan; it still costs every candidate index.
+ tests.add(new Object[] { shape[0] + ", CBO", query, true, null });
+ }
+ return tests;
+ }
+
+ private static String list(long first) {
+ return LongStream.range(first, first + LIST_LENGTH).mapToObj(Long::toString)
+ .collect(Collectors.joining(", ", "[", "]"));
+ }
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ System.setProperty(GlobalConfig.CONFIG_FILE_PROPERTY, CONFIG_FILE);
+ integrationUtil.init(true, CONFIG_FILE);
+ compile(DDL);
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ integrationUtil.deinit(true);
+ }
+
+ @Test
+ public void test() throws Exception {
+ String script = "USE test; SET `compiler.cbo` \"" + cbo + "\"; " + query;
+ long start = System.nanoTime();
+ String plan = compile(script);
+ long seconds = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start);
+ Assert.assertTrue("compiling an IN list of " + LIST_LENGTH + " took " + seconds + " s",
+ seconds < MAX_COMPILE_SECONDS);
+ if (expectedIndex != null) {
+ Assert.assertTrue("the plan does not search " + expectedIndex,
+ plan.contains("index-search(\"" + expectedIndex + "\""));
+ }
+ }
+
+ private static String compile(String script) throws Exception {
+ AsterixJavaClient client = new AsterixJavaClient(
+ (ICcApplicationContext) integrationUtil.cc.getApplicationContext(),
+ integrationUtil.getHyracksClientConnection(), new StringReader(script),
+ new PrintWriter(Writer.nullWriter()), new SqlppCompilationProvider(new NamespaceResolver(false)),
+ new DefaultStatementExecutorFactory(), new StorageComponentProvider());
+ client.compile(true, false, false, true, false, false, false, SessionConfig.PlanFormat.STRING, false);
+ return client.getExecutionPlans().getOptimizedLogicalPlan();
+ }
+}
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/btree-index-selection/disjunctive-predicate/disjunctive-predicate-4.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/btree-index-selection/disjunctive-predicate/disjunctive-predicate-4.plan
index e949647f8f..00fe176a8a 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results/btree-index-selection/disjunctive-predicate/disjunctive-predicate-4.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/btree-index-selection/disjunctive-predicate/disjunctive-predicate-4.plan
@@ -2,33 +2,29 @@ distribute result [$$x]
-- DISTRIBUTE_RESULT |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- select (or(eq($$20, "one"), $$26, $$26)) project: [$$x]
+ select (eq($$x.getField(1), $$25)) project: [$$x]
-- STREAM_SELECT |PARTITIONED|
- assign [$$26] <- [eq($$20, "two")]
- -- ASSIGN |PARTITIONED|
- assign [$$20] <- [$$x.getField(1)]
- -- ASSIGN |PARTITIONED|
- project ([$$x])
- -- STREAM_PROJECT |PARTITIONED|
+ project ([$$25, $$x])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ unnest-map [$$23, $$x] <- index-search("TestSet", 0, "Default", "test", "TestSet", true, false, 1, $$27, 1, $$27, true, true, true)
+ -- BTREE_SEARCH |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$21, $$x] <- index-search("TestSet", 0, "Default", "test", "TestSet", true, false, 1, $$25, 1, $$25, true, true, true)
- -- BTREE_SEARCH |PARTITIONED|
+ order (ASC, $$27)
+ -- STABLE_SORT [$$27(ASC)] |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$25)
- -- STABLE_SORT [$$25(ASC)] |PARTITIONED|
+ project ([$$25, $$27])
+ -- STREAM_PROJECT |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$25])
- -- STREAM_PROJECT |PARTITIONED|
+ unnest-map [$$26, $$27] <- index-search("TestSetIndex", 0, "Default", "test", "TestSet", true, true, 1, $$25, 1, $$25, true, true, true)
+ -- BTREE_SEARCH |PARTITIONED|
exchange
- -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$24, $$25] <- index-search("TestSetIndex", 0, "Default", "test", "TestSet", true, true, 1, $$23, 1, $$23, true, true, true)
- -- BTREE_SEARCH |PARTITIONED|
- exchange
- -- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$23 <- scan-collection(array: [ "one", "two" ])
- -- UNNEST |PARTITIONED|
- empty-tuple-source
- -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- BROADCAST_EXCHANGE |PARTITIONED|
+ unnest $$25 <- scan-collection(array: [ "one", "two" ])
+ -- UNNEST |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/btree-index-selection/disjunctive-predicate/disjunctive-predicate-5.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/btree-index-selection/disjunctive-predicate/disjunctive-predicate-5.plan
index d7fe68af1a..11104d51b3 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results/btree-index-selection/disjunctive-predicate/disjunctive-predicate-5.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/btree-index-selection/disjunctive-predicate/disjunctive-predicate-5.plan
@@ -8,33 +8,29 @@ distribute result [$$x]
-- RANDOM_MERGE_EXCHANGE |PARTITIONED|
limit 2
-- STREAM_LIMIT |PARTITIONED|
- select (or(eq($$22, "one"), $$28, $$28)) project: [$$x]
+ select (eq($$x.getField(1), $$25)) project: [$$x]
-- STREAM_SELECT |PARTITIONED|
- assign [$$28] <- [eq($$22, "two")]
- -- ASSIGN |PARTITIONED|
- assign [$$22] <- [$$x.getField(1)]
- -- ASSIGN |PARTITIONED|
- project ([$$x])
- -- STREAM_PROJECT |PARTITIONED|
+ project ([$$25, $$x])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ unnest-map [$$23, $$x] <- index-search("TestSet", 0, "Default", "test", "TestSet", true, false, 1, $$27, 1, $$27, true, true, true)
+ -- BTREE_SEARCH |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$23, $$x] <- index-search("TestSet", 0, "Default", "test", "TestSet", true, false, 1, $$27, 1, $$27, true, true, true)
- -- BTREE_SEARCH |PARTITIONED|
+ order (ASC, $$27)
+ -- STABLE_SORT [$$27(ASC)] |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$27)
- -- STABLE_SORT [$$27(ASC)] |PARTITIONED|
+ project ([$$25, $$27])
+ -- STREAM_PROJECT |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$27])
- -- STREAM_PROJECT |PARTITIONED|
+ unnest-map [$$26, $$27] <- index-search("TestSetIndex", 0, "Default", "test", "TestSet", true, true, 1, $$25, 1, $$25, true, true, true)
+ -- BTREE_SEARCH |PARTITIONED|
exchange
- -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$26, $$27] <- index-search("TestSetIndex", 0, "Default", "test", "TestSet", true, true, 1, $$25, 1, $$25, true, true, true)
- -- BTREE_SEARCH |PARTITIONED|
- exchange
- -- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$25 <- scan-collection(array: [ "one", "two" ])
- -- UNNEST |PARTITIONED|
- empty-tuple-source
- -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- BROADCAST_EXCHANGE |PARTITIONED|
+ unnest $$25 <- scan-collection(array: [ "one", "two" ])
+ -- UNNEST |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/btree-index-selection/disjunctive-predicate/disjunctive-predicate-7.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/btree-index-selection/disjunctive-predicate/disjunctive-predicate-7.plan
index e949647f8f..00fe176a8a 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results/btree-index-selection/disjunctive-predicate/disjunctive-predicate-7.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/btree-index-selection/disjunctive-predicate/disjunctive-predicate-7.plan
@@ -2,33 +2,29 @@ distribute result [$$x]
-- DISTRIBUTE_RESULT |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- select (or(eq($$20, "one"), $$26, $$26)) project: [$$x]
+ select (eq($$x.getField(1), $$25)) project: [$$x]
-- STREAM_SELECT |PARTITIONED|
- assign [$$26] <- [eq($$20, "two")]
- -- ASSIGN |PARTITIONED|
- assign [$$20] <- [$$x.getField(1)]
- -- ASSIGN |PARTITIONED|
- project ([$$x])
- -- STREAM_PROJECT |PARTITIONED|
+ project ([$$25, $$x])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ unnest-map [$$23, $$x] <- index-search("TestSet", 0, "Default", "test", "TestSet", true, false, 1, $$27, 1, $$27, true, true, true)
+ -- BTREE_SEARCH |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$21, $$x] <- index-search("TestSet", 0, "Default", "test", "TestSet", true, false, 1, $$25, 1, $$25, true, true, true)
- -- BTREE_SEARCH |PARTITIONED|
+ order (ASC, $$27)
+ -- STABLE_SORT [$$27(ASC)] |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$25)
- -- STABLE_SORT [$$25(ASC)] |PARTITIONED|
+ project ([$$25, $$27])
+ -- STREAM_PROJECT |PARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$25])
- -- STREAM_PROJECT |PARTITIONED|
+ unnest-map [$$26, $$27] <- index-search("TestSetIndex", 0, "Default", "test", "TestSet", true, true, 1, $$25, 1, $$25, true, true, true)
+ -- BTREE_SEARCH |PARTITIONED|
exchange
- -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$24, $$25] <- index-search("TestSetIndex", 0, "Default", "test", "TestSet", true, true, 1, $$23, 1, $$23, true, true, true)
- -- BTREE_SEARCH |PARTITIONED|
- exchange
- -- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$23 <- scan-collection(array: [ "one", "two" ])
- -- UNNEST |PARTITIONED|
- empty-tuple-source
- -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- BROADCAST_EXCHANGE |PARTITIONED|
+ unnest $$25 <- scan-collection(array: [ "one", "two" ])
+ -- UNNEST |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.1.ddl.sqlpp
new file mode 100644
index 0000000000..f65046da4b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.1.ddl.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+DROP DATAVERSE test IF EXISTS;
+CREATE DATAVERSE test;
+USE test;
+
+CREATE TYPE T AS { id: bigint };
+CREATE DATASET col1(T) PRIMARY KEY id;
+CREATE INDEX idx_a ON col1(a: bigint);
+CREATE INDEX idx_n ON col1(n: string);
+CREATE DATASET col2(T) PRIMARY KEY id;
+CREATE INDEX idx_ab ON col2(a: bigint, b: bigint);
+CREATE DATASET col3(T) PRIMARY KEY id;
+CREATE INDEX idx3_a ON col3(a: bigint);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.10.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.10.query.sqlpp
new file mode 100644
index 0000000000..80f22c8538
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.10.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* A long IN list, every key listed twice. */
+USE test;
+
+SELECT VALUE COUNT(*) FROM col1 c WHERE c.a IN [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 11 [...]
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.11.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.11.query.sqlpp
new file mode 100644
index 0000000000..d096f7cfb0
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.11.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* An index-only plan, whose condition is a copy of the query's. */
+USE test;
+
+SELECT VALUE c.a FROM col1 c WHERE c.a = 2 OR 5 = c.a OR 2 = c.a ORDER BY c.a;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.12.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.12.query.sqlpp
new file mode 100644
index 0000000000..18b6992014
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.12.query.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT VALUE c.id FROM col1 c WHERE c.id IN [3, 3, 1] ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.13.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.13.query.sqlpp
new file mode 100644
index 0000000000..1fcae428e3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.13.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* With statistics, for the cost-based optimizer to cost the index. */
+USE test;
+
+SELECT VALUE COUNT(*) FROM col3 c WHERE c.a IN [1, 1, 2, 3];
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.14.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.14.query.sqlpp
new file mode 100644
index 0000000000..f3a3389c82
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.14.query.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT VALUE COUNT(*) FROM col3 c WHERE c.a = 1 OR 2 = c.a OR c.a = 1;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.15.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.15.query.sqlpp
new file mode 100644
index 0000000000..3086b038c4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.15.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* A LIMIT counts each copy, so it cuts off records it should return. */
+USE test;
+
+SELECT VALUE c.id FROM col3 c WHERE c.a = 7 OR 7 = c.a ORDER BY c.id LIMIT 3;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.16.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.16.query.sqlpp
new file mode 100644
index 0000000000..3c35c3b215
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.16.query.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT VALUE COUNT(*) FROM col1 c WHERE c.a IN [1, 2] AND c.a IN [2, 3];
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.17.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.17.query.sqlpp
new file mode 100644
index 0000000000..73b784ffdb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.17.query.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT VALUE c.id FROM col1 c JOIN col2 d ON c.id = d.id WHERE c.a IN [1, 2] AND c.a IN [2, 3] ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.18.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.18.query.sqlpp
new file mode 100644
index 0000000000..2812217c48
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.18.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* A repeated key on the second key of a composite index. */
+USE test;
+
+SELECT VALUE c.id FROM col2 c WHERE c.a = 1 AND (c.b = 3 OR 3 = c.b) ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.19.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.19.query.sqlpp
new file mode 100644
index 0000000000..04e88269cb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.19.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* The primary index. */
+USE test;
+
+SELECT VALUE c.id FROM col1 c WHERE c.id = 3 OR 3 = c.id ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.2.update.sqlpp
new file mode 100644
index 0000000000..3ec1fa751e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.2.update.sqlpp
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+INSERT INTO col1 ([
+ {"id": 1, "a": 1, "b": 3, "n": "x"},
+ {"id": 2, "a": 1, "b": 4, "n": "y"},
+ {"id": 3, "a": 2, "b": 3, "n": "z"},
+ {"id": 4, "a": 2, "b": 5, "n": "x"},
+ {"id": 5, "a": 3, "b": 4, "n": "y"},
+ {"id": 6, "a": 4, "b": 3, "n": "z"},
+ {"id": 7, "a": 5},
+ {"id": 8}
+]);
+
+INSERT INTO col2 (SELECT VALUE c FROM col1 c);
+
+INSERT INTO col3 (SELECT VALUE {"id": i, "a": i % 100} FROM range(1, 2000) i);
+
+ANALYZE DATASET col3;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.20.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.20.query.sqlpp
new file mode 100644
index 0000000000..7d6b40112a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.20.query.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT VALUE c.id FROM col1 c WHERE c.id IN [1, 3] AND c.id IN [3, 4] ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.21.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.21.query.sqlpp
new file mode 100644
index 0000000000..4705090925
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.21.query.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT VALUE c.id FROM col1 c WHERE c.a = 1 OR 1 = c.a ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.22.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.22.query.sqlpp
new file mode 100644
index 0000000000..57f57e46e2
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.22.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* An IN list and an OR sharing a key. */
+USE test;
+
+SELECT VALUE c.id FROM col1 c WHERE c.a IN [1, 2] AND (c.a = 2 OR c.a = 3) ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.23.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.23.query.sqlpp
new file mode 100644
index 0000000000..04de718639
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.23.query.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT VALUE c.id FROM col1 c WHERE c.n = 'x' OR 'x' = c.n ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.3.query.sqlpp
new file mode 100644
index 0000000000..8c986303a8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.3.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* A key listed twice is searched for once. */
+USE test;
+
+SELECT VALUE c.id FROM col1 c WHERE c.a IN [1, 1, 2] ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.4.query.sqlpp
new file mode 100644
index 0000000000..4b9c169631
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.4.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* The same key on either side of the comparison is still one key. */
+USE test;
+
+SELECT VALUE c.id FROM col1 c WHERE c.a = 1 OR 2 = c.a OR c.a = 1 OR 1 = c.a OR 2 = c.a ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.5.query.sqlpp
new file mode 100644
index 0000000000..b0c7091548
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.5.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two IN lists on one key share a key. */
+USE test;
+
+SELECT VALUE c.id FROM col1 c WHERE c.a IN [1, 2] AND c.a IN [2, 3] ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.6.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.6.query.sqlpp
new file mode 100644
index 0000000000..e2ac5f5021
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.6.query.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT VALUE c.id FROM col1 c WHERE c.a IN [1, 2, 3] AND c.a IN [3, 1] ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.7.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.7.query.sqlpp
new file mode 100644
index 0000000000..4b2c664dbf
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.7.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* An IN list and an equality on the same key. */
+USE test;
+
+SELECT VALUE c.id FROM col1 c WHERE c.a IN [1, 2] AND c.a = 1 ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.8.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.8.query.sqlpp
new file mode 100644
index 0000000000..113fb978de
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.8.query.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE test;
+
+SELECT VALUE c.id FROM col1 c WHERE c.n IN ['x', 'y', 'x'] ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.9.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.9.query.sqlpp
new file mode 100644
index 0000000000..90c02351b8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.9.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* IN lists on both keys of a composite index. */
+USE test;
+
+SELECT VALUE c.id FROM col2 c WHERE c.a IN [1, 2] AND c.b IN [3, 4] ORDER BY c.id;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/btree-index-range-02/btree-index-range-02.16.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/btree-index-range-02/btree-index-range-02.16.plan
index 6ebf362684..90c72c4aa0 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/btree-index-range-02/btree-index-range-02.16.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/btree-index-range-02/btree-index-range-02.16.plan
@@ -18,63 +18,63 @@ distribute result [$$54] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$55, $$A] <- index-search("A", 0, "Default", "test", "A", true, false, 1, $$68, 1, $$68, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$55, $$A] <- index-search("A", 0, "Default", "test", "A", false, false, 1, $$69, 1, $$69, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- intersect [$$68] <- [[$$61], [$$64], [$$67]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ intersect [$$69] <- [[$$62], [$$65], [$$68]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- INTERSECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$61) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$61(ASC)] |PARTITIONED|
+ order (ASC, $$62) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$62(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$61]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$62]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$60, $$61] <- index-search("idx_a", 0, "Default", "test", "A", true, false, 1, $$59, 1, $$59, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$61, $$62] <- index-search("idx_a", 0, "Default", "test", "A", false, false, 1, $$59, 1, $$60, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$59] <- [1] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$59, $$60] <- [1, 1] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$64) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$64(ASC)] |PARTITIONED|
+ order (ASC, $$65) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$65(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$64]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$65]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$63, $$64] <- index-search("idx_b", 0, "Default", "test", "A", true, true, 1, $$62, 1, $$62, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$64, $$65] <- index-search("idx_b", 0, "Default", "test", "A", true, true, 1, $$63, 1, $$63, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$62 <- scan-collection(array: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$63 <- scan-collection(array: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- UNNEST |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$67) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$67(ASC)] |PARTITIONED|
+ order (ASC, $$68) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$68(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$67]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$68]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$66, $$67] <- index-search("idx_c", 0, "Default", "test", "A", true, true, 1, $$65, 1, $$65, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$67, $$68] <- index-search("idx_c", 0, "Default", "test", "A", true, true, 1, $$66, 1, $$66, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$65 <- scan-collection(array: [ 0, 1, 2, 3, 4 ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$66 <- scan-collection(array: [ 0, 1, 2, 3, 4 ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- UNNEST |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 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/index-selection/btree-index-range-02/btree-index-range-02.18.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/btree-index-range-02/btree-index-range-02.18.plan
index 9987fbab68..545ad4f115 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/btree-index-range-02/btree-index-range-02.18.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/btree-index-range-02/btree-index-range-02.18.plan
@@ -18,11 +18,11 @@ distribute result [$$50] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$51, $$A] <- index-search("A", 0, "Default", "test", "A", true, false, 1, $$65, 1, $$65, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$51, $$A] <- index-search("A", 0, "Default", "test", "A", true, false, 1, $$66, 1, $$66, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- intersect [$$65] <- [[$$58], [$$61], [$$64]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ intersect [$$66] <- [[$$58], [$$62], [$$65]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- INTERSECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
@@ -44,37 +44,37 @@ distribute result [$$50] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$61) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$61(ASC)] |PARTITIONED|
+ order (ASC, $$62) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$62(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$61]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$62]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$60, $$61] <- index-search("idx_b", 0, "Default", "test", "A", true, false, 1, $$59, 1, $$59, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$61, $$62] <- index-search("idx_b", 0, "Default", "test", "A", false, false, 1, $$59, 1, $$60, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$59] <- [15] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$59, $$60] <- [15, 15] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$64) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$64(ASC)] |PARTITIONED|
+ order (ASC, $$65) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$65(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$64]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$65]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$63, $$64] <- index-search("idx_c", 0, "Default", "test", "A", false, false, 0, 1, $$62, true, true, false) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$64, $$65] <- index-search("idx_c", 0, "Default", "test", "A", false, false, 0, 1, $$63, true, false, false) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$62] <- [3] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$63] <- [3] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 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/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.10.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.10.adm
new file mode 100644
index 0000000000..7f8f011eb7
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.10.adm
@@ -0,0 +1 @@
+7
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.11.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.11.adm
new file mode 100644
index 0000000000..ec1ddb41c2
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.11.adm
@@ -0,0 +1,3 @@
+2
+2
+5
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.12.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.12.adm
new file mode 100644
index 0000000000..2b2f2e1b92
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.12.adm
@@ -0,0 +1,2 @@
+1
+3
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.13.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.13.adm
new file mode 100644
index 0000000000..abdfb053e4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.13.adm
@@ -0,0 +1 @@
+60
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.14.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.14.adm
new file mode 100644
index 0000000000..425151f3a4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.14.adm
@@ -0,0 +1 @@
+40
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.15.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.15.adm
new file mode 100644
index 0000000000..1eaed32484
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.15.adm
@@ -0,0 +1,3 @@
+7
+107
+207
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.16.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.16.adm
new file mode 100644
index 0000000000..0cfbf08886
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.16.adm
@@ -0,0 +1 @@
+2
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.17.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.17.adm
new file mode 100644
index 0000000000..b94473479c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.17.adm
@@ -0,0 +1,2 @@
+3
+4
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.18.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.18.adm
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.18.adm
@@ -0,0 +1 @@
+1
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.19.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.19.adm
new file mode 100644
index 0000000000..00750edc07
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.19.adm
@@ -0,0 +1 @@
+3
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.20.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.20.adm
new file mode 100644
index 0000000000..00750edc07
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.20.adm
@@ -0,0 +1 @@
+3
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.21.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.21.adm
new file mode 100644
index 0000000000..1191247b6d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.21.adm
@@ -0,0 +1,2 @@
+1
+2
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.22.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.22.adm
new file mode 100644
index 0000000000..b94473479c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.22.adm
@@ -0,0 +1,2 @@
+3
+4
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.23.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.23.adm
new file mode 100644
index 0000000000..2f1b638ff5
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.23.adm
@@ -0,0 +1,2 @@
+1
+4
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.3.adm
new file mode 100644
index 0000000000..94ebaf9001
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.3.adm
@@ -0,0 +1,4 @@
+1
+2
+3
+4
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.4.adm
new file mode 100644
index 0000000000..94ebaf9001
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.4.adm
@@ -0,0 +1,4 @@
+1
+2
+3
+4
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.5.adm
new file mode 100644
index 0000000000..b94473479c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.5.adm
@@ -0,0 +1,2 @@
+3
+4
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.6.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.6.adm
new file mode 100644
index 0000000000..c7282027d1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.6.adm
@@ -0,0 +1,3 @@
+1
+2
+5
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.7.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.7.adm
new file mode 100644
index 0000000000..1191247b6d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.7.adm
@@ -0,0 +1,2 @@
+1
+2
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.8.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.8.adm
new file mode 100644
index 0000000000..7059ba5d2a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.8.adm
@@ -0,0 +1,4 @@
+1
+2
+4
+5
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.9.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.9.adm
new file mode 100644
index 0000000000..01e79c32a8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/in-list-duplicate-keys/in-list-duplicate-keys.9.adm
@@ -0,0 +1,3 @@
+1
+2
+3
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.12.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.12.plan
index 42dbe1d796..173ae70c50 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.12.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.12.plan
@@ -22,49 +22,49 @@ distribute result [$$61] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$62, $$o] <- index-search("orders", 0, "Default", "test", "orders", true, false, 1, $$72, 1, $$72, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$62, $$o] <- index-search("orders", 0, "Default", "test", "orders", false, false, 1, $$73, 1, $$73, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- intersect [$$72] <- [[$$68], [$$71]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ intersect [$$73] <- [[$$69], [$$72]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- INTERSECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- distinct ([$$68]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ distinct ([$$69]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- PRE_SORTED_DISTINCT_BY |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$68) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$68(ASC)] |PARTITIONED|
+ order (ASC, $$69) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$69(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$68]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$69]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$67, $$68] <- index-search("idx_order_tags", 0, "Default", "test", "orders", true, false, 1, $$66, 1, $$66, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$68, $$69] <- index-search("idx_order_tags", 0, "Default", "test", "orders", false, false, 1, $$66, 1, $$67, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$66] <- ["urgent"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$66, $$67] <- ["urgent", "urgent"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$71) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$71(ASC)] |PARTITIONED|
+ order (ASC, $$72) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$72(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$71]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$72]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$70, $$71] <- index-search("idx_order_status", 0, "Default", "test", "orders", true, true, 1, $$69, 1, $$69, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$71, $$72] <- index-search("idx_order_status", 0, "Default", "test", "orders", true, true, 1, $$70, 1, $$70, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$69 <- scan-collection(array: [ "pending", "shipped" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$70 <- scan-collection(array: [ "pending", "shipped" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- UNNEST |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 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/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.18.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.18.plan
index 3cc88ec13d..021d435890 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.18.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.18.plan
@@ -22,67 +22,67 @@ distribute result [$$68] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$69, $$o] <- index-search("orders", 0, "Default", "test", "orders", true, false, 1, $$83, 1, $$83, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$69, $$o] <- index-search("orders", 0, "Default", "test", "orders", false, false, 1, $$84, 1, $$84, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- intersect [$$83] <- [[$$76], [$$79], [$$82]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ intersect [$$84] <- [[$$77], [$$80], [$$83]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- INTERSECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- distinct ([$$76]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ distinct ([$$77]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- PRE_SORTED_DISTINCT_BY |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$76) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$76(ASC)] |PARTITIONED|
+ order (ASC, $$77) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$77(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$76]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$77]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$75, $$76] <- index-search("idx_orderline_delivery", 0, "Default", "test", "orders", true, false, 1, $$74, 1, $$74, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$76, $$77] <- index-search("idx_orderline_delivery", 0, "Default", "test", "orders", false, false, 1, $$74, 1, $$75, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$74] <- ["2024-01-15"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$74, $$75] <- ["2024-01-15", "2024-01-15"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$79) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$79(ASC)] |PARTITIONED|
+ order (ASC, $$80) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$80(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$79]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$80]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$78, $$79] <- index-search("idx_order_region", 0, "Default", "test", "orders", true, true, 1, $$77, 1, $$77, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$79, $$80] <- index-search("idx_order_region", 0, "Default", "test", "orders", true, true, 1, $$78, 1, $$78, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$77 <- scan-collection(array: [ "east", "central" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$78 <- scan-collection(array: [ "east", "central" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- UNNEST |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$82) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$82(ASC)] |PARTITIONED|
+ order (ASC, $$83) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$83(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$82]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$83]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$81, $$82] <- index-search("idx_order_status", 0, "Default", "test", "orders", true, true, 1, $$80, 1, $$80, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$82, $$83] <- index-search("idx_order_status", 0, "Default", "test", "orders", true, true, 1, $$81, 1, $$81, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$80 <- scan-collection(array: [ "pending", "delivered" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$81 <- scan-collection(array: [ "pending", "delivered" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- UNNEST |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 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/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.20.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.20.plan
index f4b3e9eb7d..17624abcc6 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.20.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.20.plan
@@ -22,67 +22,67 @@ distribute result [$$63] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$64, $$c] <- index-search("customers", 0, "Default", "test", "customers", true, false, 1, $$78, 1, $$78, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$64, $$c] <- index-search("customers", 0, "Default", "test", "customers", false, false, 1, $$80, 1, $$80, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- intersect [$$78] <- [[$$71], [$$74], [$$77]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ intersect [$$80] <- [[$$72], [$$75], [$$79]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- INTERSECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- distinct ([$$71]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ distinct ([$$72]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- PRE_SORTED_DISTINCT_BY |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$71) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$71(ASC)] |PARTITIONED|
+ order (ASC, $$72) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$72(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$71]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$72]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$70, $$71] <- index-search("idx_cust_interests", 0, "Default", "test", "customers", true, false, 1, $$69, 1, $$69, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$71, $$72] <- index-search("idx_cust_interests", 0, "Default", "test", "customers", false, false, 1, $$69, 1, $$70, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$69] <- ["sports"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$69, $$70] <- ["sports", "sports"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$74) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$74(ASC)] |PARTITIONED|
+ order (ASC, $$75) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$75(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$74]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$75]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$73, $$74] <- index-search("idx_cust_city", 0, "Default", "test", "customers", true, true, 1, $$72, 1, $$72, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$74, $$75] <- index-search("idx_cust_city", 0, "Default", "test", "customers", true, true, 1, $$73, 1, $$73, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$72 <- scan-collection(array: [ "New York", "Chicago", "Houston" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$73 <- scan-collection(array: [ "New York", "Chicago", "Houston" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- UNNEST |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$77) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$77(ASC)] |PARTITIONED|
+ order (ASC, $$79) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$79(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$77]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$79]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$76, $$77] <- index-search("idx_cust_tier", 0, "Default", "test", "customers", true, false, 1, $$75, 1, $$75, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$78, $$79] <- index-search("idx_cust_tier", 0, "Default", "test", "customers", false, false, 1, $$76, 1, $$77, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$75] <- ["gold"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$76, $$77] <- ["gold", "gold"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 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/index-selection/secondary-index-or-in-01/secondary-index-or-in-01.16.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-or-in-01/secondary-index-or-in-01.16.plan
index 0118ae2502..665ad2a5ad 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-or-in-01/secondary-index-or-in-01.16.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/index-selection/secondary-index-or-in-01/secondary-index-or-in-01.16.plan
@@ -34,7 +34,7 @@ distribute result [$$48] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$54, $$55] <- index-search("idx_cust_age_het", 0, "Default", "test", "customers", false, false, 1, $$53, 0, true, true, false) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$54, $$55] <- index-search("idx_cust_age_het", 0, "Default", "test", "customers", false, false, 1, $$53, 0, false, true, false) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 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/index-selection/btree-index-range-02/btree-index-range-02.18.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/index-selection/btree-index-range-02/btree-index-range-02.18.plan
index 48cc42ce7c..0b1bf145b0 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/index-selection/btree-index-range-02/btree-index-range-02.18.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/index-selection/btree-index-range-02/btree-index-range-02.18.plan
@@ -18,25 +18,23 @@ distribute result [$$50] [cardinality: 367.44, doc-size: 0.0, op-cost: 0.0, tota
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 367.44, doc-size: 0.0, op-cost: 0.0, total-cost: 734.88]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$51, $$A] <- index-search("A", 0, "Default", "test", "A", true, false, 1, $$58, 1, $$58, true, true, true) [cardinality: 367.44, doc-size: 0.0, op-cost: 0.0, total-cost: 734.88]
+ unnest-map [$$51, $$A] <- index-search("A", 0, "Default", "test", "A", false, false, 1, $$59, 1, $$59, true, true, true) [cardinality: 356.3, doc-size: 0.0, op-cost: 0.0, total-cost: 933.25]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$58) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$58(ASC)] |PARTITIONED|
+ order (ASC, $$59) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$59(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- select (eq($$57, 15)) project: [$$58] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ select (eq($$58, 15)) project: [$$59] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_SELECT |PARTITIONED|
- project ([$$57, $$58]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STREAM_PROJECT |PARTITIONED|
- exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$57, $$58] <- index-search("idx_b", 0, "Default", "test", "A", true, false, 1, $$56, 1, $$56, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- BTREE_SEARCH |PARTITIONED|
- exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$56] <- [15] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- ASSIGN |PARTITIONED|
- empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ unnest-map [$$58, $$59] <- index-search("idx_b", 0, "Default", "test", "A", false, false, 1, $$56, 1, $$57, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- BTREE_SEARCH |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$56, $$57] <- [15, 15] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ASSIGN |PARTITIONED|
+ empty-tuple-source [cardinality: 0.0, doc-size: 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_column/index-selection/btree-index-range-02/btree-index-range-02.16.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/btree-index-range-02/btree-index-range-02.16.plan
index bbd1174349..e746fe62aa 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/btree-index-range-02/btree-index-range-02.16.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/btree-index-range-02/btree-index-range-02.16.plan
@@ -18,63 +18,63 @@ distribute result [$$54] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$55, $$A] <- index-search("A", 0, "Default", "test", "A", true, false, 1, $$68, 1, $$68, true, true, true) project ({a:any,b:any,c:any}) range-filter on: and(eq($$A.getField("a"), 1), or(eq($$A.getField("b"), 0), eq($$A.getField("b"), 1), eq($$A.getField("b"), 2), eq($$A.getField("b"), 3), eq($$A.getField("b"), 4), eq($$A.getField("b"), 5), eq($$A.getField("b"), 6), eq($$A.getField("b"), 7), eq($$A.getField("b"), 8), eq($$A.getField("b"), 9), eq($$A.getFi [...]
+ unnest-map [$$55, $$A] <- index-search("A", 0, "Default", "test", "A", false, false, 1, $$69, 1, $$69, true, true, true) project ({a:any,b:any,c:any}) range-filter on: and(eq($$A.getField("a"), 1), or(eq($$A.getField("b"), 0), eq($$A.getField("b"), 1), eq($$A.getField("b"), 2), eq($$A.getField("b"), 3), eq($$A.getField("b"), 4), eq($$A.getField("b"), 5), eq($$A.getField("b"), 6), eq($$A.getField("b"), 7), eq($$A.getField("b"), 8), eq($$A.getField("b"), 9), eq($$A.getF [...]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- intersect [$$68] <- [[$$61], [$$64], [$$67]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ intersect [$$69] <- [[$$62], [$$65], [$$68]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- INTERSECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$61) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$61(ASC)] |PARTITIONED|
+ order (ASC, $$62) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$62(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$61]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$62]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$60, $$61] <- index-search("idx_a", 0, "Default", "test", "A", true, false, 1, $$59, 1, $$59, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$61, $$62] <- index-search("idx_a", 0, "Default", "test", "A", false, false, 1, $$59, 1, $$60, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$59] <- [1] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$59, $$60] <- [1, 1] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$64) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$64(ASC)] |PARTITIONED|
+ order (ASC, $$65) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$65(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$64]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$65]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$63, $$64] <- index-search("idx_b", 0, "Default", "test", "A", true, true, 1, $$62, 1, $$62, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$64, $$65] <- index-search("idx_b", 0, "Default", "test", "A", true, true, 1, $$63, 1, $$63, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$62 <- scan-collection(array: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$63 <- scan-collection(array: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- UNNEST |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$67) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$67(ASC)] |PARTITIONED|
+ order (ASC, $$68) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$68(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$67]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$68]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$66, $$67] <- index-search("idx_c", 0, "Default", "test", "A", true, true, 1, $$65, 1, $$65, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$67, $$68] <- index-search("idx_c", 0, "Default", "test", "A", true, true, 1, $$66, 1, $$66, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$65 <- scan-collection(array: [ 0, 1, 2, 3, 4 ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$66 <- scan-collection(array: [ 0, 1, 2, 3, 4 ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- UNNEST |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 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_column/index-selection/btree-index-range-02/btree-index-range-02.18.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/btree-index-range-02/btree-index-range-02.18.plan
index e3f2a9eb45..51ae3ff101 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/btree-index-range-02/btree-index-range-02.18.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/btree-index-range-02/btree-index-range-02.18.plan
@@ -18,11 +18,11 @@ distribute result [$$50] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$51, $$A] <- index-search("A", 0, "Default", "test", "A", true, false, 1, $$65, 1, $$65, true, true, true) project ({a:any,b:any,c:any}) range-filter on: and(lt($$A.getField("c"), 3), eq($$A.getField("b"), 15), or(eq($$A.getField("a"), 0), eq($$A.getField("a"), 1))) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$51, $$A] <- index-search("A", 0, "Default", "test", "A", true, false, 1, $$66, 1, $$66, true, true, true) project ({a:any,b:any,c:any}) range-filter on: and(lt($$A.getField("c"), 3), eq($$A.getField("b"), 15), or(eq($$A.getField("a"), 0), eq($$A.getField("a"), 1))) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- intersect [$$65] <- [[$$58], [$$61], [$$64]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ intersect [$$66] <- [[$$58], [$$62], [$$65]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- INTERSECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
@@ -44,37 +44,37 @@ distribute result [$$50] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$61) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$61(ASC)] |PARTITIONED|
+ order (ASC, $$62) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$62(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$61]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$62]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$60, $$61] <- index-search("idx_b", 0, "Default", "test", "A", true, false, 1, $$59, 1, $$59, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$61, $$62] <- index-search("idx_b", 0, "Default", "test", "A", false, false, 1, $$59, 1, $$60, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$59] <- [15] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$59, $$60] <- [15, 15] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$64) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$64(ASC)] |PARTITIONED|
+ order (ASC, $$65) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$65(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$64]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$65]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$63, $$64] <- index-search("idx_c", 0, "Default", "test", "A", false, false, 0, 1, $$62, true, true, false) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$64, $$65] <- index-search("idx_c", 0, "Default", "test", "A", false, false, 0, 1, $$63, true, false, false) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$62] <- [3] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$63] <- [3] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 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_column/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.12.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.12.plan
index 88e99fa14f..fa85bf61fb 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.12.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.12.plan
@@ -22,49 +22,49 @@ distribute result [$$61] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$62, $$o] <- index-search("orders", 0, "Default", "test", "orders", true, false, 1, $$72, 1, $$72, true, true, true) project ({status:any,tags:[any]}) range-filter on: and(or(eq($$o.getField("status"), "pending"), eq($$o.getField("status"), "shipped")), eq(scan-collection($$o.getField("tags")), "urgent")) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$62, $$o] <- index-search("orders", 0, "Default", "test", "orders", false, false, 1, $$73, 1, $$73, true, true, true) project ({status:any,tags:[any]}) range-filter on: and(or(eq($$o.getField("status"), "pending"), eq($$o.getField("status"), "shipped")), eq(scan-collection($$o.getField("tags")), "urgent")) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- intersect [$$72] <- [[$$68], [$$71]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ intersect [$$73] <- [[$$69], [$$72]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- INTERSECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- distinct ([$$68]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ distinct ([$$69]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- PRE_SORTED_DISTINCT_BY |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$68) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$68(ASC)] |PARTITIONED|
+ order (ASC, $$69) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$69(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$68]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$69]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$67, $$68] <- index-search("idx_order_tags", 0, "Default", "test", "orders", true, false, 1, $$66, 1, $$66, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$68, $$69] <- index-search("idx_order_tags", 0, "Default", "test", "orders", false, false, 1, $$66, 1, $$67, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$66] <- ["urgent"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$66, $$67] <- ["urgent", "urgent"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$71) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$71(ASC)] |PARTITIONED|
+ order (ASC, $$72) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$72(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$71]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$72]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$70, $$71] <- index-search("idx_order_status", 0, "Default", "test", "orders", true, true, 1, $$69, 1, $$69, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$71, $$72] <- index-search("idx_order_status", 0, "Default", "test", "orders", true, true, 1, $$70, 1, $$70, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$69 <- scan-collection(array: [ "pending", "shipped" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$70 <- scan-collection(array: [ "pending", "shipped" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- UNNEST |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 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_column/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.18.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.18.plan
index 76d136401b..e01cdcb934 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.18.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.18.plan
@@ -22,67 +22,67 @@ distribute result [$$68] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$69, $$o] <- index-search("orders", 0, "Default", "test", "orders", true, false, 1, $$83, 1, $$83, true, true, true) project ({region:any,o_orderline:[{ol_delivery_d:any}],status:any}) range-filter on: and(or(eq($$o.getField("status"), "pending"), eq($$o.getField("status"), "delivered")), or(eq($$o.getField("region"), "east"), eq($$o.getField("region"), "central")), eq(scan-collection($$o.getField("o_orderline")).getField("ol_delivery_d"), "2024-01-15 [...]
+ unnest-map [$$69, $$o] <- index-search("orders", 0, "Default", "test", "orders", false, false, 1, $$84, 1, $$84, true, true, true) project ({region:any,o_orderline:[{ol_delivery_d:any}],status:any}) range-filter on: and(or(eq($$o.getField("status"), "pending"), eq($$o.getField("status"), "delivered")), or(eq($$o.getField("region"), "east"), eq($$o.getField("region"), "central")), eq(scan-collection($$o.getField("o_orderline")).getField("ol_delivery_d"), "2024-01-1 [...]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- intersect [$$83] <- [[$$76], [$$79], [$$82]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ intersect [$$84] <- [[$$77], [$$80], [$$83]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- INTERSECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- distinct ([$$76]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ distinct ([$$77]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- PRE_SORTED_DISTINCT_BY |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$76) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$76(ASC)] |PARTITIONED|
+ order (ASC, $$77) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$77(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$76]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$77]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$75, $$76] <- index-search("idx_orderline_delivery", 0, "Default", "test", "orders", true, false, 1, $$74, 1, $$74, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$76, $$77] <- index-search("idx_orderline_delivery", 0, "Default", "test", "orders", false, false, 1, $$74, 1, $$75, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$74] <- ["2024-01-15"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$74, $$75] <- ["2024-01-15", "2024-01-15"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$79) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$79(ASC)] |PARTITIONED|
+ order (ASC, $$80) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$80(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$79]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$80]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$78, $$79] <- index-search("idx_order_region", 0, "Default", "test", "orders", true, true, 1, $$77, 1, $$77, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$79, $$80] <- index-search("idx_order_region", 0, "Default", "test", "orders", true, true, 1, $$78, 1, $$78, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$77 <- scan-collection(array: [ "east", "central" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$78 <- scan-collection(array: [ "east", "central" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- UNNEST |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$82) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$82(ASC)] |PARTITIONED|
+ order (ASC, $$83) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$83(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$82]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$83]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$81, $$82] <- index-search("idx_order_status", 0, "Default", "test", "orders", true, true, 1, $$80, 1, $$80, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$82, $$83] <- index-search("idx_order_status", 0, "Default", "test", "orders", true, true, 1, $$81, 1, $$81, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$80 <- scan-collection(array: [ "pending", "delivered" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$81 <- scan-collection(array: [ "pending", "delivered" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- UNNEST |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 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_column/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.20.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.20.plan
index 0a4a5d2e80..3809d7b9a6 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.20.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/secondary-index-intersection-01/secondary-index-intersection-01.20.plan
@@ -22,67 +22,67 @@ distribute result [$$63] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$64, $$c] <- index-search("customers", 0, "Default", "test", "customers", true, false, 1, $$78, 1, $$78, true, true, true) project ({tier:any,city:any,interests:[any]}) range-filter on: and(eq($$c.getField("tier"), "gold"), or(eq($$c.getField("city"), "New York"), eq($$c.getField("city"), "Chicago"), eq($$c.getField("city"), "Houston")), eq(scan-collection($$c.getField("interests")), "sports")) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$64, $$c] <- index-search("customers", 0, "Default", "test", "customers", false, false, 1, $$80, 1, $$80, true, true, true) project ({tier:any,city:any,interests:[any]}) range-filter on: and(eq($$c.getField("tier"), "gold"), or(eq($$c.getField("city"), "New York"), eq($$c.getField("city"), "Chicago"), eq($$c.getField("city"), "Houston")), eq(scan-collection($$c.getField("interests")), "sports")) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-co [...]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- intersect [$$78] <- [[$$71], [$$74], [$$77]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ intersect [$$80] <- [[$$72], [$$75], [$$79]] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- INTERSECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- distinct ([$$71]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ distinct ([$$72]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- PRE_SORTED_DISTINCT_BY |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$71) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$71(ASC)] |PARTITIONED|
+ order (ASC, $$72) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$72(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$71]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$72]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$70, $$71] <- index-search("idx_cust_interests", 0, "Default", "test", "customers", true, false, 1, $$69, 1, $$69, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$71, $$72] <- index-search("idx_cust_interests", 0, "Default", "test", "customers", false, false, 1, $$69, 1, $$70, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$69] <- ["sports"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$69, $$70] <- ["sports", "sports"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$74) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$74(ASC)] |PARTITIONED|
+ order (ASC, $$75) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$75(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$74]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$75]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$73, $$74] <- index-search("idx_cust_city", 0, "Default", "test", "customers", true, true, 1, $$72, 1, $$72, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$74, $$75] <- index-search("idx_cust_city", 0, "Default", "test", "customers", true, true, 1, $$73, 1, $$73, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BROADCAST_EXCHANGE |PARTITIONED|
- unnest $$72 <- scan-collection(array: [ "New York", "Chicago", "Houston" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest $$73 <- scan-collection(array: [ "New York", "Chicago", "Houston" ]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- UNNEST |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- EMPTY_TUPLE_SOURCE |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- order (ASC, $$77) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
- -- STABLE_SORT [$$77(ASC)] |PARTITIONED|
+ order (ASC, $$79) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STABLE_SORT [$$79(ASC)] |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- project ([$$77]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ project ([$$79]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$76, $$77] <- index-search("idx_cust_tier", 0, "Default", "test", "customers", true, false, 1, $$75, 1, $$75, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$78, $$79] <- index-search("idx_cust_tier", 0, "Default", "test", "customers", false, false, 1, $$76, 1, $$77, true, true, true) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- assign [$$75] <- ["gold"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ assign [$$76, $$77] <- ["gold", "gold"] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ASSIGN |PARTITIONED|
empty-tuple-source [cardinality: 0.0, doc-size: 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_column/index-selection/secondary-index-or-in-01/secondary-index-or-in-01.16.plan b/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/secondary-index-or-in-01/secondary-index-or-in-01.16.plan
index 85ab8597e9..2937529338 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/secondary-index-or-in-01/secondary-index-or-in-01.16.plan
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_column/index-selection/secondary-index-or-in-01/secondary-index-or-in-01.16.plan
@@ -34,7 +34,7 @@ distribute result [$$48] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-c
-- STREAM_PROJECT |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- unnest-map [$$54, $$55] <- index-search("idx_cust_age_het", 0, "Default", "test", "customers", false, false, 1, $$53, 0, true, true, false) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ unnest-map [$$54, $$55] <- index-search("idx_cust_age_het", 0, "Default", "test", "customers", false, false, 1, $$53, 0, false, true, false) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
-- BTREE_SEARCH |PARTITIONED|
exchange [cardinality: 0.0, doc-size: 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/sqlpp_queries.xml b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
index 3bd78a59ed..adb7d47340 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
@@ -6753,6 +6753,11 @@
<expected-warn><![CDATA[ASX1132: Invalid specification for hint use-index. ASX1001: Syntax error: In line 1 >>()<< Encountered ")" at column 2. (in line 33, at column 15)]]></expected-warn>
</compilation-unit>
</test-case>
+ <test-case FilePath="index-selection">
+ <compilation-unit name="in-list-duplicate-keys">
+ <output-dir compare="Text">in-list-duplicate-keys</output-dir>
+ </compilation-unit>
+ </test-case>
<test-case FilePath="index-selection">
<compilation-unit name="inverted-index-ngram-edit-distance-with-two-ngram-index">
<output-dir compare="Text">inverted-index-ngram-edit-distance-with-two-ngram-index</output-dir>