You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@asterixdb.apache.org by AsterixDB Code Review <do...@asterix-gerrit.ics.uci.edu> on 2023/05/25 18:09:12 UTC
Change in asterixdb[master]: [ASTERIXDB-3197][COMP] outer join support for CBO
From <mu...@couchbase.com>:
murali.krishna@couchbase.com has uploaded this change for review. ( https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/17557 )
Change subject: [ASTERIXDB-3197][COMP] outer join support for CBO
......................................................................
[ASTERIXDB-3197][COMP] outer join support for CBO
Change-Id: I70067ceb9817965a79d45f786548c6496a9e3853
---
M asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/PlanNode.java
M asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinCondition.java
M hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/LogicalVariable.java
M asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/extract-common-operators/extract-common-operators.01.plan
M hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractBinaryJoinOperator.java
A asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/leftouterjoin/query-ASTERIXDB-2857.plan
A asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/intersection-misc/intersection-misc-02.plan
M asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java
M asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinNode.java
M hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LeftOuterJoinOperator.java
A asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/intersection-misc/intersection-misc-03.plan
M asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
A asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/intersection-misc/intersection-misc-01.plan
A asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/ASTERIXDB-2402.plan
14 files changed, 959 insertions(+), 120 deletions(-)
git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb refs/changes/57/17557/1
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
index f2319c4..42e26f6 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
@@ -34,6 +34,7 @@
import org.apache.commons.lang3.mutable.MutableObject;
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.algebricks.common.utils.Pair;
+import org.apache.hyracks.algebricks.common.utils.Quadruple;
import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
@@ -68,6 +69,9 @@
private static final Logger LOGGER = LogManager.getLogger();
private final JoinEnum joinEnum;
+ private int leafInputNumber;
+ List<ILogicalOperator> newJoinOps = new ArrayList<>();
+ boolean[] unUsedJoinOps;
public EnumerateJoinsRule(JoinEnum joinEnum) {
this.joinEnum = joinEnum;
@@ -91,6 +95,7 @@
if (!(cboMode || cboTestMode)) {
return false;
}
+
// If we reach here, then either cboMode or cboTestMode is true.
// If cboTestMode is true, then we use predefined cardinalities for datasets for asterixdb regression tests.
// If cboMode is true, then all datasets need to have samples, otherwise the check in doAllDataSourcesHaveSamples()
@@ -110,17 +115,21 @@
// The data scan operators. Will be in the order of the from clause.
// Important for position ordering when assigning bits to join expressions.
List<Pair<EmptyTupleSourceOperator, DataSourceScanOperator>> emptyTupleAndDataSourceOps = new ArrayList<>();
+ List<Quadruple<Integer, Integer, ILogicalOperator, Integer>> outerJoinsDependencyList = new ArrayList<>();
List<AssignOperator> assignOps = new ArrayList<>();
IPlanPrettyPrinter pp = context.getPrettyPrinter();
printPlan(pp, (AbstractLogicalOperator) op, "Original Whole plan1");
- boolean canTransform =
- getJoinOpsAndLeafInputs(op, emptyTupleAndDataSourceOps, joinLeafInputsHashMap, joinOps, assignOps);
+ leafInputNumber = 0;
+ boolean canTransform = getJoinOpsAndLeafInputs(op, emptyTupleAndDataSourceOps, joinLeafInputsHashMap, joinOps,
+ assignOps, outerJoinsDependencyList);
if (!canTransform) {
return false;
}
+ //convertOuterJoinstoJoinsIfPossible(outerJoinsDependencyList);
+
// if this happens, something in the input plan is not acceptable to the new code.
if (emptyTupleAndDataSourceOps.size() != joinLeafInputsHashMap.size()) {
throw new IllegalStateException(
@@ -132,7 +141,8 @@
int numberOfFromTerms = emptyTupleAndDataSourceOps.size();
joinEnum.initEnum((AbstractLogicalOperator) op, cboMode, cboTestMode, numberOfFromTerms,
- emptyTupleAndDataSourceOps, joinLeafInputsHashMap, joinOps, assignOps, context);
+ emptyTupleAndDataSourceOps, joinLeafInputsHashMap, joinOps, assignOps, outerJoinsDependencyList,
+ context);
if (cboMode) {
if (!doAllDataSourcesHaveSamples(emptyTupleAndDataSourceOps, context)) {
@@ -154,7 +164,9 @@
printLeafPlans(pp, joinLeafInputsHashMap);
}
+ //System.out.println("calling joinEnum.enumerateJoins()");
int cheapestPlan = joinEnum.enumerateJoins(); // MAIN CALL INTO CBO
+ //System.out.println("cheapest plan is " + cheapestPlan);
if (cheapestPlan == PlanNode.NO_PLAN) {
return false;
}
@@ -164,26 +176,35 @@
generateHintWarnings();
if (numberOfFromTerms > 1) {
- buildNewTree(cheapestPlanNode, joinLeafInputsHashMap, joinOps, new MutableInt(0));
- printPlan(pp, (AbstractLogicalOperator) joinOps.get(0), "New Whole Plan after buildNewTree 1");
- ILogicalOperator root = addRemainingAssignsAtTheTop(joinOps.get(0), assignOps);
- printPlan(pp, (AbstractLogicalOperator) joinOps.get(0), "New Whole Plan after buildNewTree 2");
+ unUsedJoinOps = new boolean[joinOps.size()];
+ for (int i = 0; i < joinOps.size(); i++)
+ unUsedJoinOps[i] = true;
+ getNewJoinOps(cheapestPlanNode, joinOps);
+ if (joinOps.size() != newJoinOps.size()) {
+ return false; // there are some cases such as R OJ S on true. Here there is an OJ predicate but the code in findJoinConditions
+ // in JoinEnum does not capture this. Will fix later. Just bail for now.
+ }
+ buildNewTree(cheapestPlanNode, joinLeafInputsHashMap, newJoinOps, new MutableInt(0));
+ printPlan(pp, (AbstractLogicalOperator) newJoinOps.get(0), "New Whole Plan after buildNewTree 1");
+ ILogicalOperator root = addRemainingAssignsAtTheTop(newJoinOps.get(0), assignOps);
+ printPlan(pp, (AbstractLogicalOperator) newJoinOps.get(0), "New Whole Plan after buildNewTree 2");
printPlan(pp, (AbstractLogicalOperator) root, "New Whole Plan after buildNewTree");
+
// this will be the new root
opRef.setValue(root);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("---------------------------- Printing Leaf Inputs");
printLeafPlans(pp, joinLeafInputsHashMap);
// print joins starting from the bottom
- for (int i = joinOps.size() - 1; i >= 0; i--) {
- printPlan(pp, (AbstractLogicalOperator) joinOps.get(i), "join " + i);
+ for (int i = newJoinOps.size() - 1; i >= 0; i--) {
+ printPlan(pp, (AbstractLogicalOperator) newJoinOps.get(i), "join " + i);
}
- printPlan(pp, (AbstractLogicalOperator) joinOps.get(0), "New Whole Plan");
+ printPlan(pp, (AbstractLogicalOperator) newJoinOps.get(0), "New Whole Plan");
printPlan(pp, (AbstractLogicalOperator) root, "New Whole Plan");
}
// turn of this rule for all joins in this set (subtree)
- for (ILogicalOperator joinOp : joinOps) {
+ for (ILogicalOperator joinOp : newJoinOps) {
context.addToDontApplySet(this, joinOp);
}
@@ -197,8 +218,8 @@
private boolean joinClause(ILogicalOperator op) {
if (op.getOperatorTag() == LogicalOperatorTag.INNERJOIN)
return true;
- //if (op.getOperatorTag() == LogicalOperatorTag.LEFTOUTERJOIN)
- //return true;
+ if (op.getOperatorTag() == LogicalOperatorTag.LEFTOUTERJOIN)
+ return true;
return false;
}
@@ -317,6 +338,118 @@
}
}
+ private boolean addLeafInputNumbersToVars(ILogicalOperator op) throws AlgebricksException {
+ HashSet<LogicalVariable> opVars = new HashSet<>();
+ VariableUtilities.getLiveVariables(op, opVars);
+ for (LogicalVariable lv : opVars) {
+ int id = lv.getLeafInputId();
+ if ((id != -1) && (id != leafInputNumber)) {
+ return false; // this should not happen
+ // the same variable in different leaf Inputs is problematic for CBO
+ }
+ lv.setLeafInputId(leafInputNumber);
+ }
+ return true;
+ }
+
+ private boolean foundVar(LogicalVariable inputLV, ILogicalOperator op) throws AlgebricksException {
+ HashSet<LogicalVariable> opVars = new HashSet<>();
+ VariableUtilities.getLiveVariables(op, opVars);
+ if (opVars.contains(inputLV)) { // note that this will fail if there variables from different leafInputs
+ return true;
+ }
+ return false;
+ }
+
+ // dependencylist is first, second, op
+ // If we have R outer join S, first is the null extending table as in R, null
+ // In this case, if S is to joined, then R must be present. So S depends on R.
+ // If we have a case of (first, second, LOJ_operator) = (R_leaf_input_id, S_leaf_input_id, LOJop),
+ // and another (S_leaf_input_id, ..., joinOp),
+ // OR (..., S_leaf_input_id, joinOp) then the LOJ can be converted to a join!!
+ private void convertOuterJoinstoJoinsIfPossible(
+ List<Quadruple<Integer, Integer, ILogicalOperator, Integer>> outerJoinsDependencyList) {
+
+ List<Integer> getRidOff = new ArrayList<>();
+ int i;
+ //for (Triple<Integer, Integer, ILogicalOperator> tr1 : outerJoinsDependencyList) {
+ for (i = 0; i < outerJoinsDependencyList.size(); i++) {
+ Quadruple<Integer, Integer, ILogicalOperator, Integer> tr1 = outerJoinsDependencyList.get(i);
+ if (tr1.getThird().getOperatorTag() == LogicalOperatorTag.LEFTOUTERJOIN) {
+ for (Quadruple<Integer, Integer, ILogicalOperator, Integer> tr2 : outerJoinsDependencyList) {
+ if (tr2.getThird().getOperatorTag() == LogicalOperatorTag.INNERJOIN) {
+ if ((tr1.getSecond().equals(tr2.getFirst())) || (tr1.getSecond().equals(tr2.getSecond()))) {
+ getRidOff.add(i);
+ }
+ }
+ }
+ }
+ }
+
+ for (i = getRidOff.size() - 1; i >= 0; i--) {
+ int j = getRidOff.get(i);
+ AbstractBinaryJoinOperator joinOp = (AbstractBinaryJoinOperator) outerJoinsDependencyList.get(j).getThird();
+ joinOp.setOuterJoin(false);
+ outerJoinsDependencyList.remove(j);
+ }
+ }
+
+ // Each outer join will create one set of dependencies. The right side depends on the left side.
+ private boolean buildDependencyList(ILogicalOperator op,
+ List<Quadruple<Integer, Integer, ILogicalOperator, Integer>> outerJoinsDependencyList)
+ throws AlgebricksException {
+ AbstractBinaryJoinOperator outerJoinOp = (AbstractBinaryJoinOperator) op;
+ ILogicalOperator leftOp = op.getInputs().get(0).getValue();
+ ILogicalExpression expr = outerJoinOp.getCondition().getValue();
+ int leftSideExprBits, rightSideExprBits;
+ List<LogicalVariable> joinExprVars;
+ List<Mutable<ILogicalExpression>> conjs = new ArrayList<>();
+ if (expr.splitIntoConjuncts(conjs)) {
+ for (Mutable<ILogicalExpression> conj : conjs) {
+ joinExprVars = new ArrayList<>();
+ leftSideExprBits = 0;
+ rightSideExprBits = 0;
+ conj.getValue().getUsedVariables(joinExprVars);
+ for (LogicalVariable lv : joinExprVars) {
+ if (foundVar(lv, leftOp)) {
+ leftSideExprBits |= 1 << (lv.getLeafInputId() - 1);
+ } else {
+ rightSideExprBits |= 1 << (lv.getLeafInputId() - 1);
+ }
+ }
+ if (leftSideExprBits != 0 && rightSideExprBits != 0) {// avoid expressions like true
+ outerJoinsDependencyList.add(new Quadruple(leftSideExprBits, rightSideExprBits, op, 1));
+ }
+ }
+ } else {
+ leftSideExprBits = 0;
+ rightSideExprBits = 0;
+ joinExprVars = new ArrayList<>();
+ expr.getUsedVariables(joinExprVars);
+ for (LogicalVariable lv : joinExprVars) {
+ if (foundVar(lv, leftOp)) {
+ leftSideExprBits |= 1 << (lv.getLeafInputId() - 1);
+ } else {
+ rightSideExprBits |= 1 << (lv.getLeafInputId() - 1);
+ }
+ }
+ if (leftSideExprBits != 0 && rightSideExprBits != 0) {// avoid expressions like true
+ outerJoinsDependencyList.add(new Quadruple(leftSideExprBits, rightSideExprBits, op, 1));
+ }
+ }
+ return true;
+ }
+
+ private boolean joinFound(ILogicalOperator op) {
+ while (op.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
+ if (joinClause(op)) {
+ return true;
+ }
+ op = op.getInputs().get(0).getValue();
+ }
+ return false;
+ }
+
/**
* This is the main routine that stores all the join operators and the leafInputs. We will later reuse the same
* join operators but switch the leafInputs (see buildNewTree). The whole scheme is based on the assumption that the
@@ -325,21 +458,30 @@
private boolean getJoinOpsAndLeafInputs(ILogicalOperator op,
List<Pair<EmptyTupleSourceOperator, DataSourceScanOperator>> emptyTupleAndDataSourceOps,
HashMap<EmptyTupleSourceOperator, ILogicalOperator> joinLeafInputsHashMap, List<ILogicalOperator> joinOps,
- List<AssignOperator> assignOps) {
-
- if (op.getOperatorTag() == LogicalOperatorTag.LEFTOUTERJOIN) {
- return false;
- }
+ List<AssignOperator> assignOps,
+ List<Quadruple<Integer, Integer, ILogicalOperator, Integer>> outerJoinsDependencyList)
+ throws AlgebricksException {
if (joinClause(op)) {
joinOps.add(op);
+
for (int i = 0; i < 2; i++) {
ILogicalOperator nextOp = op.getInputs().get(i).getValue();
boolean canTransform = getJoinOpsAndLeafInputs(nextOp, emptyTupleAndDataSourceOps,
- joinLeafInputsHashMap, joinOps, assignOps);
+ joinLeafInputsHashMap, joinOps, assignOps, outerJoinsDependencyList);
if (!canTransform) {
return false;
}
+ if ((op.getOperatorTag() == LogicalOperatorTag.LEFTOUTERJOIN) && (i == 1)) {
+ // see if there are joins on the rhs; If there are, bail for now // will address bushy plans soon
+ if (joinFound(nextOp))
+ return false;
+ }
+
+ }
+ boolean ret = buildDependencyList(op, outerJoinsDependencyList);
+ if (!ret) {
+ return false;
}
} else {
Pair<EmptyTupleSourceOperator, DataSourceScanOperator> etsDataSource = containsLeafInputOnly(op);
@@ -355,7 +497,11 @@
joinLeafInputsHashMap.put(etsOp, selectOp);
}
} else {
+ leafInputNumber++;
joinLeafInputsHashMap.put(etsOp, op);
+ if (!addLeafInputNumbersToVars(op)) {
+ return false;
+ }
}
} else { // This must be an internal edge
if (onlyAssigns(op, assignOps)) {
@@ -365,7 +511,7 @@
ILogicalOperator skipAssisgnsOp = skipPastAssigns(op);
boolean canTransform = getJoinOpsAndLeafInputs(skipAssisgnsOp, emptyTupleAndDataSourceOps,
- joinLeafInputsHashMap, joinOps, assignOps);
+ joinLeafInputsHashMap, joinOps, assignOps, outerJoinsDependencyList);
if (!canTransform) {
return false;
}
@@ -374,7 +520,6 @@
}
}
}
-
return true;
}
@@ -465,10 +610,7 @@
if (plan.scanOp == PlanNode.ScanMethod.TABLE_SCAN && leafInput.getOperatorTag() == LogicalOperatorTag.SELECT) {
SelectOperator selOper = (SelectOperator) leafInput;
ILogicalExpression expr = selOper.getCondition().getValue();
-
List<Mutable<ILogicalExpression>> conjs = new ArrayList<>();
-
- conjs.clear();
if (expr.splitIntoConjuncts(conjs)) {
conjs.remove(new MutableObject<ILogicalExpression>(ConstantExpression.TRUE));
for (Mutable<ILogicalExpression> conj : conjs) {
@@ -502,9 +644,36 @@
addCardCostAnnotations(findDataSourceScanOperator(leftInput), plan);
}
+ private void getJoinNode(PlanNode plan, List<ILogicalOperator> joinOps) throws AlgebricksException {
+ //AbstractBinaryJoinOperator joinOp;
+ LogicalOperatorTag tag;
+ if (plan.outerJoin) {
+ tag = LogicalOperatorTag.LEFTOUTERJOIN;
+ } else {
+ tag = LogicalOperatorTag.INNERJOIN;
+ }
+ int i = -1;
+ for (ILogicalOperator joinOp : joinOps) {
+ i++;
+ if (joinOp.getOperatorTag() == tag && unUsedJoinOps[i]) {
+ unUsedJoinOps[i] = false;
+ newJoinOps.add(joinOp);
+ break;
+ }
+ }
+ }
+
+ private void getNewJoinOps(PlanNode plan, List<ILogicalOperator> oldJoinOps) throws AlgebricksException {
+ if (plan.IsJoinNode()) {
+ getJoinNode(plan, oldJoinOps);
+ getNewJoinOps(plan.getLeftPlanNode(), oldJoinOps);
+ getNewJoinOps(plan.getRightPlanNode(), oldJoinOps);
+ }
+ }
+
// This one is for join queries
private void buildNewTree(PlanNode plan, HashMap<EmptyTupleSourceOperator, ILogicalOperator> joinLeafInputsHashMap,
- List<ILogicalOperator> joinOps, MutableInt totalNumberOfJoins) {
+ List<ILogicalOperator> joinOps, MutableInt totalNumberOfJoins) throws AlgebricksException {
// we have to move the inputs in op around so that they match the tree structure in pn
// we use the existing joinOps and switch the leafInputs appropriately.
List<PlanNode> allPlans = joinEnum.getAllPlans();
@@ -512,7 +681,7 @@
int rightIndex = plan.getRightPlanIndex();
PlanNode leftPlan = allPlans.get(leftIndex);
PlanNode rightPlan = allPlans.get(rightIndex);
- ILogicalOperator joinOp = joinOps.get(totalNumberOfJoins.intValue());
+ ILogicalOperator joinOp = joinOps.get(totalNumberOfJoins.intValue()); // intValue set to 0 initially
if (plan.IsJoinNode()) {
AbstractBinaryJoinOperator abJoinOp = (AbstractBinaryJoinOperator) joinOp;
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinCondition.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinCondition.java
index 7cf1ebd..d56d38a 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinCondition.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinCondition.java
@@ -26,6 +26,7 @@
protected static final int NO_JC = -1;
protected ILogicalExpression joinCondition;
+ protected boolean outerJoin;
private boolean derived = false;
protected boolean partOfComposite = false;
protected int numberOfVars = 0; // how many variables
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java
index 46a67e8..fef72d2 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java
@@ -52,6 +52,7 @@
import org.apache.commons.lang3.mutable.MutableObject;
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.algebricks.common.utils.Pair;
+import org.apache.hyracks.algebricks.common.utils.Quadruple;
import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
@@ -96,8 +97,9 @@
private List<Pair<EmptyTupleSourceOperator, DataSourceScanOperator>> emptyTupleAndDataSourceOps;
protected Map<EmptyTupleSourceOperator, ILogicalOperator> joinLeafInputsHashMap;
protected List<ILogicalExpression> singleDatasetPreds;
- private List<AssignOperator> assignOps;
- private List<ILogicalOperator> joinOps;
+ protected List<AssignOperator> assignOps;
+ List<Quadruple<Integer, Integer, ILogicalOperator, Integer>> outerJoinsDependencyList;
+ protected List<ILogicalOperator> joinOps;
protected ILogicalOperator localJoinOp; // used in nestedLoopsApplicable code.
protected IOptimizationContext optCtx;
@@ -118,7 +120,9 @@
protected void initEnum(AbstractLogicalOperator op, boolean cboMode, boolean cboTestMode, int numberOfFromTerms,
List<Pair<EmptyTupleSourceOperator, DataSourceScanOperator>> emptyTupleAndDataSourceOps,
Map<EmptyTupleSourceOperator, ILogicalOperator> joinLeafInputsHashMap, List<ILogicalOperator> joinOps,
- List<AssignOperator> assignOps, IOptimizationContext context) {
+ List<AssignOperator> assignOps,
+ List<Quadruple<Integer, Integer, ILogicalOperator, Integer>> outerJoinsDependencyList,
+ IOptimizationContext context) {
this.singleDatasetPreds = new ArrayList<>();
this.joinConditions = new ArrayList<>();
this.joinHints = new HashMap<>();
@@ -131,6 +135,7 @@
this.emptyTupleAndDataSourceOps = emptyTupleAndDataSourceOps;
this.joinLeafInputsHashMap = joinLeafInputsHashMap;
this.assignOps = assignOps;
+ this.outerJoinsDependencyList = outerJoinsDependencyList;
this.joinOps = joinOps;
this.op = op;
this.forceJoinOrderMode = getForceJoinOrderMode(context);
@@ -186,7 +191,6 @@
return op;
}
}
- // this will never happen, but keep compiler happy
return null;
}
@@ -248,7 +252,19 @@
return eqPredFound ? andExpr : null;
}
- protected HashJoinExpressionAnnotation findHashJoinHint(List<Integer> newJoinConditions) {
+ public boolean lookForOuterJoins(List<Integer> newJoinConditions) {
+
+ for (int joinNum : newJoinConditions) {
+ JoinCondition jc = joinConditions.get(joinNum);
+ if (jc.outerJoin) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public HashJoinExpressionAnnotation findHashJoinHint(List<Integer> newJoinConditions) {
for (int i : newJoinConditions) {
JoinCondition jc = joinConditions.get(i);
if (jc.comparisonType != JoinCondition.comparisonOp.OP_EQ) {
@@ -327,7 +343,6 @@
return i;
}
}
- // should never happen; keep compiler happy.
return JoinNode.NO_JN;
}
@@ -369,7 +384,6 @@
}
return bits;
}
- // should never reach this because every variable must exist in some leaf input.
return JoinNode.NO_JN;
}
@@ -385,13 +399,15 @@
conjs.remove(new MutableObject<ILogicalExpression>(ConstantExpression.TRUE));
for (Mutable<ILogicalExpression> conj : conjs) {
JoinCondition jc = new JoinCondition();
+ jc.outerJoin = joinOp.getOuterJoin();
jc.joinCondition = conj.getValue().cloneExpression();
joinConditions.add(jc);
jc.selectivity = stats.getSelectivityFromAnnotationMain(jc.joinCondition, true);
}
} else {
- if ((expr.getExpressionTag().equals(LogicalExpressionTag.FUNCTION_CALL))) {
+ if ((expr.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL)) {
JoinCondition jc = new JoinCondition();
+ jc.outerJoin = joinOp.getOuterJoin();
// change to not a true condition
jc.joinCondition = expr.cloneExpression();
joinConditions.add(jc);
@@ -537,6 +553,51 @@
return dataRecVarInScan.toString().substring(2);
}
+ /*
+ private boolean joinPredicate (int l, int r) {
+ for (JoinCondition jc: this.joinConditions) {
+ if (((jc.leftSideBits & l) > 0) && ((jc.rightSideBits & r) > 0)) {
+ return true;
+ }
+ if (((jc.leftSideBits & r) > 0) && ((jc.rightSideBits & l) > 0)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ */
+
+ private boolean outerJoinPossible(JoinNode leftJn, JoinNode rightJn) {
+
+ // first check if the dependent table(s) and the other tables(s) have already been joined.
+ // If so, return true (in the sense this join order can be allowed
+ // rightJn will only have one table (zig zag only). This may change when we allow for bushy trees
+
+ for (Quadruple<Integer, Integer, ILogicalOperator, Integer> tr : outerJoinsDependencyList) {
+ if (tr.getThird().getOperatorTag() == LogicalOperatorTag.LEFTOUTERJOIN) {
+ if ((rightJn.datasetBits & tr.getSecond()) > 0) { // A dependent table(s) is being joined. Find if other table(s) is present
+ if (!((leftJn.datasetBits & tr.getFirst()) > 0)) {
+ return false; // required table not found
+ }
+ }
+ }
+ }
+
+ if (leftJn.level == 1) { // if we are at a higher level, there is nothing to check as these tables have been joined already in leftJn
+ for (Quadruple<Integer, Integer, ILogicalOperator, Integer> tr : outerJoinsDependencyList) {
+ if (tr.getThird().getOperatorTag() == LogicalOperatorTag.LEFTOUTERJOIN) {
+ if ((leftJn.datasetBits & tr.getSecond()) > 0) { // A dependent table(s) is being joined. Find if other table(s) is present
+ if (!((rightJn.datasetBits & tr.getFirst()) > 0)) {
+ return false; // required table not found
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
private int addNonBushyJoinNodes(int level, int jnNumber, int[] startJnAtLevel) throws AlgebricksException {
// adding joinNodes of level (2, 3, ..., numberOfTerms)
int startJnSecondLevel = startJnAtLevel[2];
@@ -565,6 +626,12 @@
// these already have some common table
continue;
}
+ //System.out.println("Before i = " + i + " j = " + j);
+ if (!outerJoinPossible(jnI, jnJ)) {
+ continue;
+ }
+ //System.out.println("After i = " + i + " j = " + j);
+ //System.out.println("Jni Dataset bits = " + jnI.datasetBits + " Jni Dataset bits = " + jnJ.datasetBits);
int newBits = jnI.datasetBits | jnJ.datasetBits;
JoinNode jnNewBits = jnArray[newBits];
jnNewBits.jnArrayIndex = newBits;
@@ -606,6 +673,7 @@
JoinNode jnIJ = jnArray[addPlansToThisJn];
jnIJ.jnArrayIndex = addPlansToThisJn;
+
jnIJ.addMultiDatasetPlans(jnI, jnJ);
if (forceJoinOrderMode) {
break;
@@ -943,6 +1011,7 @@
markCompositeJoinPredicates();
int lastJnNum = enumerateHigherLevelJoinNodes();
JoinNode lastJn = jnArray[lastJnNum];
+ //System.out.println(dumpJoinNodes(lastJnNum));
if (LOGGER.isTraceEnabled()) {
EnumerateJoinsRule.printPlan(pp, op, "Original Whole plan in JN END");
LOGGER.trace(dumpJoinNodes(lastJnNum));
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 fa2a144..dcc284c 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
@@ -683,8 +683,8 @@
}
}
- private int buildHashJoinPlan(JoinNode leftJn, JoinNode rightJn, ILogicalExpression hashJoinExpr,
- HashJoinExpressionAnnotation hintHashJoin) {
+ protected int buildHashJoinPlan(JoinNode leftJn, JoinNode rightJn, ILogicalExpression hashJoinExpr,
+ HashJoinExpressionAnnotation hintHashJoin, boolean outerJoin) {
List<PlanNode> allPlans = joinEnum.allPlans;
PlanNode pn;
ICost hjCost, leftExchangeCost, rightExchangeCost, childCosts, totalCost;
@@ -719,11 +719,12 @@
if (this.cheapestPlanIndex == PlanNode.NO_PLAN || totalCost.costLT(this.cheapestPlanCost)
|| hintHashJoin != null) {
pn = new PlanNode(allPlans.size(), joinEnum);
- pn.setJoinNode(this);
- pn.setLeftJoinIndex(leftJn.jnArrayIndex);
- pn.setRightJoinIndex(rightJn.jnArrayIndex);
- pn.setLeftPlanIndex(leftPlan);
- pn.setRightPlanIndex(rightPlan);
+ pn.jn = this;
+ pn.outerJoin = outerJoin;
+ pn.jnIndexes[0] = leftJn.jnArrayIndex;
+ pn.jnIndexes[1] = rightJn.jnArrayIndex;
+ pn.planIndexes[0] = leftPlan;
+ pn.planIndexes[1] = rightPlan;
pn.joinOp = PlanNode.JoinMethod.HYBRID_HASH_JOIN; // need to check that all the conditions have equality predicates ONLY.
pn.joinHint = hintHashJoin;
pn.side = HashJoinExpressionAnnotation.BuildSide.RIGHT;
@@ -741,8 +742,8 @@
return PlanNode.NO_PLAN;
}
- private int buildBroadcastHashJoinPlan(JoinNode leftJn, JoinNode rightJn, ILogicalExpression hashJoinExpr,
- BroadcastExpressionAnnotation hintBroadcastHashJoin) {
+ protected int buildBroadcastHashJoinPlan(JoinNode leftJn, JoinNode rightJn, ILogicalExpression hashJoinExpr,
+ BroadcastExpressionAnnotation hintBroadcastHashJoin, boolean outerJoin) {
List<PlanNode> allPlans = joinEnum.allPlans;
PlanNode pn;
ICost bcastHjCost, leftExchangeCost, rightExchangeCost, childCosts, totalCost;
@@ -778,11 +779,12 @@
if (this.cheapestPlanIndex == PlanNode.NO_PLAN || totalCost.costLT(this.cheapestPlanCost)
|| hintBroadcastHashJoin != null) {
pn = new PlanNode(allPlans.size(), joinEnum);
- pn.setJoinNode(this);
- pn.setLeftJoinIndex(leftJn.jnArrayIndex);
- pn.setRightJoinIndex(rightJn.jnArrayIndex);
- pn.setLeftPlanIndex(leftPlan);
- pn.setRightPlanIndex(rightPlan);
+ pn.jn = this;
+ pn.outerJoin = outerJoin;
+ pn.jnIndexes[0] = leftJn.jnArrayIndex;
+ pn.jnIndexes[1] = rightJn.jnArrayIndex;
+ pn.planIndexes[0] = leftPlan;
+ pn.planIndexes[1] = rightPlan;
pn.joinOp = PlanNode.JoinMethod.BROADCAST_HASH_JOIN; // need to check that all the conditions have equality predicates ONLY.
pn.joinHint = hintBroadcastHashJoin;
pn.side = HashJoinExpressionAnnotation.BuildSide.RIGHT;
@@ -801,8 +803,8 @@
return PlanNode.NO_PLAN;
}
- private int buildNLJoinPlan(JoinNode leftJn, JoinNode rightJn, ILogicalExpression nestedLoopJoinExpr,
- IndexedNLJoinExpressionAnnotation hintNLJoin) throws AlgebricksException {
+ protected int buildNLJoinPlan(JoinNode leftJn, JoinNode rightJn, ILogicalExpression nestedLoopJoinExpr,
+ IndexedNLJoinExpressionAnnotation hintNLJoin, boolean outerJoin) throws AlgebricksException {
// Build a nested loops plan, first check if it is possible
// left right order must be preserved and right side should be a single data set
List<PlanNode> allPlans = joinEnum.allPlans;
@@ -831,11 +833,12 @@
if (this.cheapestPlanIndex == PlanNode.NO_PLAN || totalCost.costLT(this.cheapestPlanCost)
|| hintNLJoin != null) {
pn = new PlanNode(allPlans.size(), joinEnum);
- pn.setJoinNode(this);
- pn.setLeftJoinIndex(leftJn.jnArrayIndex);
- pn.setRightJoinIndex(rightJn.jnArrayIndex);
- pn.setLeftPlanIndex(leftPlan);
- pn.setRightPlanIndex(rightPlan);
+ pn.jn = this;
+ pn.outerJoin = outerJoin;
+ pn.jnIndexes[0] = leftJn.jnArrayIndex;
+ pn.jnIndexes[1] = rightJn.jnArrayIndex;
+ pn.planIndexes[0] = leftPlan;
+ pn.planIndexes[1] = rightPlan;
pn.joinOp = PlanNode.JoinMethod.INDEX_NESTED_LOOP_JOIN;
pn.joinHint = hintNLJoin;
pn.joinExpr = nestedLoopJoinExpr; // save it so can be used to add the NESTED annotation in getNewTree.
@@ -851,7 +854,7 @@
}
private int buildCPJoinPlan(JoinNode leftJn, JoinNode rightJn, ILogicalExpression hashJoinExpr,
- ILogicalExpression nestedLoopJoinExpr) {
+ ILogicalExpression nestedLoopJoinExpr, boolean outerJoin) {
// Now build a cartesian product nested loops plan
List<PlanNode> allPlans = joinEnum.allPlans;
PlanNode pn;
@@ -888,6 +891,7 @@
if (this.cheapestPlanIndex == PlanNode.NO_PLAN || totalCost.costLT(this.cheapestPlanCost)) {
pn = new PlanNode(allPlans.size(), joinEnum);
pn.setJoinNode(this);
+ pn.outerJoin = outerJoin;
pn.setLeftJoinIndex(leftJn.jnArrayIndex);
pn.setRightJoinIndex(rightJn.jnArrayIndex);
pn.setLeftPlanIndex(leftPlan);
@@ -921,7 +925,7 @@
List<Integer> newJoinConditions = this.getNewJoinConditionsOnly(); // these will be a subset of applicable join conditions.
ILogicalExpression hashJoinExpr = joinEnum.getHashJoinExpr(newJoinConditions);
ILogicalExpression nestedLoopJoinExpr = joinEnum.getNestedLoopJoinExpr(newJoinConditions);
-
+ boolean outerJoin = joinEnum.lookForOuterJoins(newJoinConditions);
if ((newJoinConditions.size() == 0) && joinEnum.connectedJoinGraph) {
// at least one plan must be there at each level as the graph is fully connected.
if (leftJn.cardinality * rightJn.cardinality > 10000.0) {
@@ -963,12 +967,13 @@
|| rightJn.aliases.contains(buildOrProbeObject)))
|| (probe && (leftJn.datasetNames.contains(buildOrProbeObject)
|| leftJn.aliases.contains(buildOrProbeObject)))) {
- hjPlan = buildHashJoinPlan(leftJn, rightJn, hashJoinExpr, hintHashJoin);
+ hjPlan = buildHashJoinPlan(leftJn, rightJn, hashJoinExpr, hintHashJoin, outerJoin);
} else if ((build && (leftJn.datasetNames.contains(buildOrProbeObject)
|| leftJn.aliases.contains(buildOrProbeObject)))
|| (probe && (rightJn.datasetNames.contains(buildOrProbeObject)
|| rightJn.aliases.contains(buildOrProbeObject)))) {
- commutativeHjPlan = buildHashJoinPlan(rightJn, leftJn, hashJoinExpr, hintHashJoin);
+ if (!outerJoin)
+ commutativeHjPlan = buildHashJoinPlan(rightJn, leftJn, hashJoinExpr, hintHashJoin, outerJoin);
}
}
if (hjPlan == PlanNode.NO_PLAN && commutativeHjPlan == PlanNode.NO_PLAN) {
@@ -984,21 +989,27 @@
(build ? "build " : "probe ") + "with " + buildOrProbeObject));
}
}
- hjPlan = buildHashJoinPlan(leftJn, rightJn, hashJoinExpr, null);
+ hjPlan = buildHashJoinPlan(leftJn, rightJn, hashJoinExpr, null, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeHjPlan = buildHashJoinPlan(rightJn, leftJn, hashJoinExpr, null);
+ if (!outerJoin)
+ commutativeHjPlan = buildHashJoinPlan(rightJn, leftJn, hashJoinExpr, null, outerJoin);
}
- bcastHjPlan = buildBroadcastHashJoinPlan(leftJn, rightJn, hashJoinExpr, null);
+ bcastHjPlan = buildBroadcastHashJoinPlan(leftJn, rightJn, hashJoinExpr, null, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeBcastHjPlan = buildBroadcastHashJoinPlan(rightJn, leftJn, hashJoinExpr, null);
+ if (!outerJoin)
+ commutativeBcastHjPlan =
+ buildBroadcastHashJoinPlan(rightJn, leftJn, hashJoinExpr, null, outerJoin);
}
- nljPlan = buildNLJoinPlan(leftJn, rightJn, nestedLoopJoinExpr, null);
+ nljPlan = buildNLJoinPlan(leftJn, rightJn, nestedLoopJoinExpr, null, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeNljPlan = buildNLJoinPlan(rightJn, leftJn, nestedLoopJoinExpr, null);
+ if (!outerJoin)
+ commutativeNljPlan = buildNLJoinPlan(rightJn, leftJn, nestedLoopJoinExpr, null, outerJoin);
}
- cpPlan = buildCPJoinPlan(leftJn, rightJn, hashJoinExpr, nestedLoopJoinExpr);
+ cpPlan = buildCPJoinPlan(leftJn, rightJn, hashJoinExpr, nestedLoopJoinExpr, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeCpPlan = buildCPJoinPlan(rightJn, leftJn, hashJoinExpr, nestedLoopJoinExpr);
+ if (!outerJoin)
+ commutativeCpPlan =
+ buildCPJoinPlan(rightJn, leftJn, hashJoinExpr, nestedLoopJoinExpr, outerJoin);
}
}
} else if (hintBroadcastHashJoin != null) {
@@ -1012,17 +1023,21 @@
if (validBroadcastObject) {
joinEnum.joinHints.put(hintBroadcastHashJoin, null);
if (rightJn.datasetNames.contains(broadcastObject) || rightJn.aliases.contains(broadcastObject)) {
- bcastHjPlan = buildBroadcastHashJoinPlan(leftJn, rightJn, hashJoinExpr, hintBroadcastHashJoin);
+ bcastHjPlan =
+ buildBroadcastHashJoinPlan(leftJn, rightJn, hashJoinExpr, hintBroadcastHashJoin, outerJoin);
} else if (leftJn.datasetNames.contains(broadcastObject) || leftJn.aliases.contains(broadcastObject)) {
- commutativeBcastHjPlan =
- buildBroadcastHashJoinPlan(rightJn, leftJn, hashJoinExpr, hintBroadcastHashJoin);
+ if (!outerJoin)
+ commutativeBcastHjPlan = buildBroadcastHashJoinPlan(rightJn, leftJn, hashJoinExpr,
+ hintBroadcastHashJoin, outerJoin);
}
} else if (broadcastObject == null) {
joinEnum.joinHints.put(hintBroadcastHashJoin, null);
- bcastHjPlan = buildBroadcastHashJoinPlan(leftJn, rightJn, hashJoinExpr, hintBroadcastHashJoin);
+ bcastHjPlan =
+ buildBroadcastHashJoinPlan(leftJn, rightJn, hashJoinExpr, hintBroadcastHashJoin, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeBcastHjPlan =
- buildBroadcastHashJoinPlan(rightJn, leftJn, hashJoinExpr, hintBroadcastHashJoin);
+ if (!outerJoin)
+ commutativeBcastHjPlan = buildBroadcastHashJoinPlan(rightJn, leftJn, hashJoinExpr,
+ hintBroadcastHashJoin, outerJoin);
}
}
if (bcastHjPlan == PlanNode.NO_PLAN && commutativeBcastHjPlan == PlanNode.NO_PLAN) {
@@ -1039,28 +1054,35 @@
}
}
- hjPlan = buildHashJoinPlan(leftJn, rightJn, hashJoinExpr, null);
+ hjPlan = buildHashJoinPlan(leftJn, rightJn, hashJoinExpr, null, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeHjPlan = buildHashJoinPlan(rightJn, leftJn, hashJoinExpr, null);
+ if (!outerJoin)
+ commutativeHjPlan = buildHashJoinPlan(rightJn, leftJn, hashJoinExpr, null, outerJoin);
}
- bcastHjPlan = buildBroadcastHashJoinPlan(leftJn, rightJn, hashJoinExpr, null);
+ bcastHjPlan = buildBroadcastHashJoinPlan(leftJn, rightJn, hashJoinExpr, null, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeBcastHjPlan = buildBroadcastHashJoinPlan(rightJn, leftJn, hashJoinExpr, null);
+ if (!outerJoin)
+ commutativeBcastHjPlan =
+ buildBroadcastHashJoinPlan(rightJn, leftJn, hashJoinExpr, null, outerJoin);
}
- nljPlan = buildNLJoinPlan(leftJn, rightJn, nestedLoopJoinExpr, null);
+ nljPlan = buildNLJoinPlan(leftJn, rightJn, nestedLoopJoinExpr, null, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeNljPlan = buildNLJoinPlan(rightJn, leftJn, nestedLoopJoinExpr, null);
+ if (!outerJoin)
+ commutativeNljPlan = buildNLJoinPlan(rightJn, leftJn, nestedLoopJoinExpr, null, outerJoin);
}
- cpPlan = buildCPJoinPlan(leftJn, rightJn, hashJoinExpr, nestedLoopJoinExpr);
+ cpPlan = buildCPJoinPlan(leftJn, rightJn, hashJoinExpr, nestedLoopJoinExpr, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeCpPlan = buildCPJoinPlan(rightJn, leftJn, hashJoinExpr, nestedLoopJoinExpr);
+ if (!outerJoin)
+ commutativeCpPlan =
+ buildCPJoinPlan(rightJn, leftJn, hashJoinExpr, nestedLoopJoinExpr, outerJoin);
}
}
} else if (hintNLJoin != null) {
joinEnum.joinHints.put(hintNLJoin, null);
- nljPlan = buildNLJoinPlan(leftJn, rightJn, nestedLoopJoinExpr, hintNLJoin);
+ nljPlan = buildNLJoinPlan(leftJn, rightJn, nestedLoopJoinExpr, hintNLJoin, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeNljPlan = buildNLJoinPlan(rightJn, leftJn, nestedLoopJoinExpr, hintNLJoin);
+ if (!outerJoin)
+ commutativeNljPlan = buildNLJoinPlan(rightJn, leftJn, nestedLoopJoinExpr, hintNLJoin, outerJoin);
}
if (nljPlan == PlanNode.NO_PLAN && commutativeNljPlan == PlanNode.NO_PLAN) {
// Hints are attached to predicates, so newJoinConditions should not be empty, but adding the check to be safe.
@@ -1074,35 +1096,44 @@
ErrorCode.INAPPLICABLE_HINT, "index nested loop join", "ignored"));
}
}
- hjPlan = buildHashJoinPlan(leftJn, rightJn, hashJoinExpr, null);
+ hjPlan = buildHashJoinPlan(leftJn, rightJn, hashJoinExpr, null, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeHjPlan = buildHashJoinPlan(rightJn, leftJn, hashJoinExpr, null);
+ if (!outerJoin)
+ commutativeHjPlan = buildHashJoinPlan(rightJn, leftJn, hashJoinExpr, null, outerJoin);
}
- bcastHjPlan = buildBroadcastHashJoinPlan(leftJn, rightJn, hashJoinExpr, null);
+ bcastHjPlan = buildBroadcastHashJoinPlan(leftJn, rightJn, hashJoinExpr, null, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeBcastHjPlan = buildBroadcastHashJoinPlan(rightJn, leftJn, hashJoinExpr, null);
+ if (!outerJoin)
+ commutativeBcastHjPlan =
+ buildBroadcastHashJoinPlan(rightJn, leftJn, hashJoinExpr, null, outerJoin);
}
- cpPlan = buildCPJoinPlan(leftJn, rightJn, hashJoinExpr, nestedLoopJoinExpr);
+ cpPlan = buildCPJoinPlan(leftJn, rightJn, hashJoinExpr, nestedLoopJoinExpr, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeCpPlan = buildCPJoinPlan(rightJn, leftJn, hashJoinExpr, nestedLoopJoinExpr);
+ if (!outerJoin)
+ commutativeCpPlan =
+ buildCPJoinPlan(rightJn, leftJn, hashJoinExpr, nestedLoopJoinExpr, outerJoin);
}
}
} else {
- hjPlan = buildHashJoinPlan(leftJn, rightJn, hashJoinExpr, null);
+ hjPlan = buildHashJoinPlan(leftJn, rightJn, hashJoinExpr, null, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeHjPlan = buildHashJoinPlan(rightJn, leftJn, hashJoinExpr, null);
+ if (!outerJoin)
+ commutativeHjPlan = buildHashJoinPlan(rightJn, leftJn, hashJoinExpr, null, outerJoin);
}
- bcastHjPlan = buildBroadcastHashJoinPlan(leftJn, rightJn, hashJoinExpr, null);
+ bcastHjPlan = buildBroadcastHashJoinPlan(leftJn, rightJn, hashJoinExpr, null, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeBcastHjPlan = buildBroadcastHashJoinPlan(rightJn, leftJn, hashJoinExpr, null);
+ if (!outerJoin)
+ commutativeBcastHjPlan = buildBroadcastHashJoinPlan(rightJn, leftJn, hashJoinExpr, null, outerJoin);
}
- nljPlan = buildNLJoinPlan(leftJn, rightJn, nestedLoopJoinExpr, null);
+ nljPlan = buildNLJoinPlan(leftJn, rightJn, nestedLoopJoinExpr, null, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeNljPlan = buildNLJoinPlan(rightJn, leftJn, nestedLoopJoinExpr, null);
+ if (!outerJoin)
+ commutativeNljPlan = buildNLJoinPlan(rightJn, leftJn, nestedLoopJoinExpr, null, outerJoin);
}
- cpPlan = buildCPJoinPlan(leftJn, rightJn, hashJoinExpr, nestedLoopJoinExpr);
+ cpPlan = buildCPJoinPlan(leftJn, rightJn, hashJoinExpr, nestedLoopJoinExpr, outerJoin);
if (!joinEnum.forceJoinOrderMode) {
- commutativeCpPlan = buildCPJoinPlan(rightJn, leftJn, hashJoinExpr, nestedLoopJoinExpr);
+ if (!outerJoin)
+ commutativeCpPlan = buildCPJoinPlan(rightJn, leftJn, hashJoinExpr, nestedLoopJoinExpr, outerJoin);
}
}
@@ -1189,6 +1220,7 @@
sb.append("Printing Join expr ").append('\n');
if (pn.joinExpr != null) {
sb.append(pn.joinExpr).append('\n');
+ sb.append("outer join " + pn.outerJoin).append('\n');
} else {
sb.append("null").append('\n');
}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/PlanNode.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/PlanNode.java
index 7ccd435..48eb80e 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/PlanNode.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/PlanNode.java
@@ -32,6 +32,7 @@
protected static int NO_PLAN = -1;
private final JoinEnum joinEnum;
+ boolean outerJoin = false;
protected int allPlansIndex;
protected int[] planIndexes;
protected int[] jnIndexes;
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/ASTERIXDB-2402.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/ASTERIXDB-2402.plan
new file mode 100644
index 0000000..33d5a4e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/ASTERIXDB-2402.plan
@@ -0,0 +1,168 @@
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SUBPLAN |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- ASSIGN |LOCAL|
+ -- MICRO_PRE_CLUSTERED_GROUP_BY[$$230] |LOCAL|
+ {
+ -- AGGREGATE |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- MICRO_STABLE_SORT [$$230(ASC)] |LOCAL|
+ -- ASSIGN |LOCAL|
+ -- UNNEST |LOCAL|
+ -- SUBPLAN |LOCAL|
+ {
+ -- AGGREGATE |LOCAL|
+ -- ASSIGN |LOCAL|
+ -- UNNEST |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- STREAM_PROJECT |PARTITIONED|
+ -- COMMIT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- INSERT_DELETE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$214] |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- PRE_CLUSTERED_GROUP_BY[$$267] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STABLE_SORT [$$267(ASC)] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$267] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$266][$$237] |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- RUNNING_AGGREGATE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- UNNEST |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- PRE_CLUSTERED_GROUP_BY[$$319] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- MICRO_PRE_CLUSTERED_GROUP_BY[$$321, $$322] |LOCAL|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- STREAM_SELECT |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STABLE_SORT [$$319(ASC), $$321(ASC), $$322(ASC)] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$319] |PARTITIONED|
+ -- UNION_ALL |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- BTREE_SEARCH (channels.Shelters.Shelters) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SPLIT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- RTREE_SEARCH (channels.Shelters.s_location) |PARTITIONED|
+ -- BROADCAST_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- NESTED_LOOP |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- NESTED_LOOP |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (channels.EmergenciesNearMeChannelChannelSubscriptions) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- BROADCAST_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (channels.Reports) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- BROADCAST_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (channels.UserLocations) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- SPLIT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- RTREE_SEARCH (channels.Shelters.s_location) |PARTITIONED|
+ -- BROADCAST_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- NESTED_LOOP |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- NESTED_LOOP |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (channels.EmergenciesNearMeChannelChannelSubscriptions) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- BROADCAST_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (channels.Reports) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- BROADCAST_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (channels.UserLocations) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$237] |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$248, $$250][$$239, $$240] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$248, $$250] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (channels.EmergenciesNearMeChannelBrokerSubscriptions) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$239, $$240] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (channels.Broker) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/intersection-misc/intersection-misc-01.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/intersection-misc/intersection-misc-01.plan
new file mode 100644
index 0000000..bbaa5d5
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/intersection-misc/intersection-misc-01.plan
@@ -0,0 +1,109 @@
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SORT_MERGE_EXCHANGE [$$142(ASC), $$132(ASC), $$144(ASC) ] |PARTITIONED|
+ -- STABLE_SORT [$$142(ASC), $$132(ASC), $$144(ASC)] |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$137][$$144] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$137] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$142][$$136] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$142] |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- PRE_CLUSTERED_GROUP_BY[$$130] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STABLE_SORT [$$130(ASC)] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$130] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$139][$$85] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$139] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.d) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$85] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.c) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$136] |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.d) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$144] |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- PRE_CLUSTERED_GROUP_BY[$$133] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STABLE_SORT [$$133(ASC)] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$133] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$140][$$104] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$140] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- BTREE_SEARCH (test.d.d) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STABLE_SORT [$$155(ASC)] |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- BTREE_SEARCH (test.d.idx_c6) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$85] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.c) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/intersection-misc/intersection-misc-02.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/intersection-misc/intersection-misc-02.plan
new file mode 100644
index 0000000..ee0f744
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/intersection-misc/intersection-misc-02.plan
@@ -0,0 +1,106 @@
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SORT_MERGE_EXCHANGE [$$142(ASC), $$132(ASC), $$144(ASC) ] |PARTITIONED|
+ -- STABLE_SORT [$$142(ASC), $$132(ASC), $$144(ASC)] |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$137][$$144] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$137] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$142][$$136] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$142] |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- PRE_CLUSTERED_GROUP_BY[$$130] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STABLE_SORT [$$130(ASC)] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$130] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$139][$$85] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$139] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.d) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$85] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.c) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$136] |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.d) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$144] |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- PRE_CLUSTERED_GROUP_BY[$$133] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STABLE_SORT [$$133(ASC)] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$133] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$140][$$104] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$140] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.d) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$85] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.c) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/intersection-misc/intersection-misc-03.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/intersection-misc/intersection-misc-03.plan
new file mode 100644
index 0000000..bbaa5d5
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/btree-index-selection/intersection-misc/intersection-misc-03.plan
@@ -0,0 +1,109 @@
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- SORT_MERGE_EXCHANGE [$$142(ASC), $$132(ASC), $$144(ASC) ] |PARTITIONED|
+ -- STABLE_SORT [$$142(ASC), $$132(ASC), $$144(ASC)] |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$137][$$144] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$137] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$142][$$136] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$142] |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- PRE_CLUSTERED_GROUP_BY[$$130] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STABLE_SORT [$$130(ASC)] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$130] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$139][$$85] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$139] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.d) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$85] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.c) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$136] |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.d) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$144] |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- PRE_CLUSTERED_GROUP_BY[$$133] |PARTITIONED|
+ {
+ -- AGGREGATE |LOCAL|
+ -- STREAM_SELECT |LOCAL|
+ -- NESTED_TUPLE_SOURCE |LOCAL|
+ }
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STABLE_SORT [$$133(ASC)] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$133] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$140][$$104] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$140] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- STREAM_SELECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- BTREE_SEARCH (test.d.d) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STABLE_SORT [$$155(ASC)] |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- BTREE_SEARCH (test.d.idx_c6) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- REPLICATE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$85] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (test.c) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/extract-common-operators/extract-common-operators.01.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/extract-common-operators/extract-common-operators.01.plan
index 29aafbf..4c84a72 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/extract-common-operators/extract-common-operators.01.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/extract-common-operators/extract-common-operators.01.plan
@@ -28,21 +28,25 @@
-- HASH_PARTITION_EXCHANGE [$$408] |PARTITIONED|
-- STREAM_PROJECT |PARTITIONED|
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- -- HYBRID_HASH_JOIN [$$408][$$ds_name] |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$408, $$407][$$ds_name, $$dv_name] |PARTITIONED|
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
-- REPLICATE |PARTITIONED|
- -- RANDOM_PARTITION_EXCHANGE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$408, $$407] |PARTITIONED|
-- STREAM_PROJECT |PARTITIONED|
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- -- BTREE_SEARCH (Metadata.Dataset.Dataset) |PARTITIONED|
- -- BROADCAST_EXCHANGE |PARTITIONED|
- -- UNNEST |UNPARTITIONED|
- -- EMPTY_TUPLE_SOURCE |UNPARTITIONED|
- -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- -- REPLICATE |PARTITIONED|
- -- BROADCAST_EXCHANGE |PARTITIONED|
+ -- DATASOURCE_SCAN (Metadata.Dataset) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$ds_name, $$dv_name] |PARTITIONED|
+ -- NESTED_LOOP |UNPARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
-- UNNEST |UNPARTITIONED|
-- EMPTY_TUPLE_SOURCE |UNPARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ -- REPLICATE |UNPARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ -- UNNEST |UNPARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |UNPARTITIONED|
-- HASH_PARTITION_EXCHANGE [$$410] |PARTITIONED|
-- STREAM_PROJECT |PARTITIONED|
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
@@ -67,24 +71,28 @@
-- HASH_PARTITION_EXCHANGE [$$412] |PARTITIONED|
-- STREAM_PROJECT |PARTITIONED|
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- -- HYBRID_HASH_JOIN [$$412][$$ds_name] |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$412, $$411][$$ds_name, $$dv_name] |PARTITIONED|
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
-- STREAM_PROJECT |PARTITIONED|
-- ASSIGN |PARTITIONED|
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
-- REPLICATE |PARTITIONED|
- -- RANDOM_PARTITION_EXCHANGE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$408, $$407] |PARTITIONED|
-- STREAM_PROJECT |PARTITIONED|
-- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- -- BTREE_SEARCH (Metadata.Dataset.Dataset) |PARTITIONED|
- -- BROADCAST_EXCHANGE |PARTITIONED|
- -- UNNEST |UNPARTITIONED|
- -- EMPTY_TUPLE_SOURCE |UNPARTITIONED|
- -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- -- STREAM_PROJECT |PARTITIONED|
- -- ASSIGN |PARTITIONED|
- -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
- -- REPLICATE |PARTITIONED|
- -- BROADCAST_EXCHANGE |PARTITIONED|
- -- UNNEST |UNPARTITIONED|
- -- EMPTY_TUPLE_SOURCE |UNPARTITIONED|
+ -- DATASOURCE_SCAN (Metadata.Dataset) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$ds_name, $$dv_name] |PARTITIONED|
+ -- NESTED_LOOP |UNPARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ -- STREAM_PROJECT |UNPARTITIONED|
+ -- ASSIGN |UNPARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ -- REPLICATE |UNPARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ -- UNNEST |UNPARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |UNPARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ -- UNNEST |UNPARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/leftouterjoin/query-ASTERIXDB-2857.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/leftouterjoin/query-ASTERIXDB-2857.plan
new file mode 100644
index 0000000..1c15dcb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/leftouterjoin/query-ASTERIXDB-2857.plan
@@ -0,0 +1,38 @@
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- SORT_MERGE_EXCHANGE [$$145(ASC), $$146(ASC), $#3(ASC) ] |PARTITIONED|
+ -- STABLE_SORT [$$145(ASC), $$146(ASC), $#3(ASC)] |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- NESTED_LOOP |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- HYBRID_HASH_JOIN [$$136][$$137] |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$136] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- BTREE_SEARCH (test.tenk.tenk) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- HASH_PARTITION_EXCHANGE [$$137] |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- BTREE_SEARCH (test.tenk.tenk) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ -- BROADCAST_EXCHANGE |PARTITIONED|
+ -- STREAM_PROJECT |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- BTREE_SEARCH (test.tenk.tenk) |PARTITIONED|
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ -- ASSIGN |PARTITIONED|
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/LogicalVariable.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/LogicalVariable.java
index f3b9edc..75e4065 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/LogicalVariable.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/LogicalVariable.java
@@ -26,6 +26,7 @@
public final class LogicalVariable {
private final int id;
private final String displayName;
+ private int leafInputId = -1; // used by CBO
public LogicalVariable(int id) {
this.id = id;
@@ -41,6 +42,14 @@
return id;
}
+ public int getLeafInputId() {
+ return leafInputId;
+ }
+
+ public void setLeafInputId(int id) {
+ this.leafInputId = id;
+ }
+
@Override
public String toString() {
return displayName;
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractBinaryJoinOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractBinaryJoinOperator.java
index a9f8cfe..f366ded 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractBinaryJoinOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractBinaryJoinOperator.java
@@ -31,6 +31,7 @@
public abstract class AbstractBinaryJoinOperator extends AbstractLogicalOperator {
protected final Mutable<ILogicalExpression> condition;
protected JoinKind joinKind;
+ protected boolean outerJoin = false; // used by CBO only
public enum JoinKind {
INNER,
@@ -57,6 +58,14 @@
return joinKind;
}
+ public void setOuterJoin(boolean flag) {
+ outerJoin = flag;
+ }
+
+ public boolean getOuterJoin() {
+ return outerJoin;
+ }
+
@Override
public void recomputeSchema() {
schema = new ArrayList<LogicalVariable>();
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LeftOuterJoinOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LeftOuterJoinOperator.java
index d709184..f99dfa3 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LeftOuterJoinOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LeftOuterJoinOperator.java
@@ -47,12 +47,14 @@
public LeftOuterJoinOperator(Mutable<ILogicalExpression> condition, IAlgebricksConstantValue missingValue) {
super(JoinKind.LEFT_OUTER, condition);
setMissingValue(missingValue);
+ outerJoin = true; // used only by CBO
}
public LeftOuterJoinOperator(Mutable<ILogicalExpression> condition, Mutable<ILogicalOperator> input1,
Mutable<ILogicalOperator> input2, IAlgebricksConstantValue missingValue) {
super(JoinKind.LEFT_OUTER, condition, input1, input2);
setMissingValue(missingValue);
+ outerJoin = true; // used only by CBO
}
public IAlgebricksConstantValue getMissingValue() {
--
To view, visit https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/17557
To unsubscribe, or for help writing mail filters, visit https://asterix-gerrit.ics.uci.edu/settings
Gerrit-Project: asterixdb
Gerrit-Branch: master
Gerrit-Change-Id: I70067ceb9817965a79d45f786548c6496a9e3853
Gerrit-Change-Number: 17557
Gerrit-PatchSet: 1
Gerrit-Owner: murali.krishna@couchbase.com
Gerrit-MessageType: newchange