in asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java [130:370]
public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
throws AlgebricksException {
boolean cboMode = this.getCBOMode(context);
boolean cboTestMode = this.getCBOTestMode(context);
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()
// further below will return false.
ILogicalOperator op = opRef.getValue();
if (!(joinClause(op) || ((op.getOperatorTag() == LogicalOperatorTag.DISTRIBUTE_RESULT)))) {
return false;
}
if (op.getOperatorTag() == LogicalOperatorTag.DISTRIBUTE_RESULT) {
// If cboMode or cboTestMode is true, identify each DistinctOp or GroupByOp for the corresponding DataScanOp
getDistinctOpsForJoinNodes(op, context);
// Find the order by op, so we can annotate cost/cards
findOrderByOp(op);
// Find the topmost assign, so we can find all the final projected variables.
ILogicalOperator tmp = op;
while (tmp.getOperatorTag() != LogicalOperatorTag.EMPTYTUPLESOURCE) {
if (tmp.getOperatorTag().equals(LogicalOperatorTag.ASSIGN)) {
addAllAssignExprVars(resultAndJoinVars, (AssignOperator) tmp);
break;
}
tmp = tmp.getInputs().get(0).getValue();
}
}
// if this join has already been seen before, no need to apply the rule again
if (context.checkIfInDontApplySet(this, op)) {
return false;
}
IPlanPrettyPrinter pp = context.getPrettyPrinter();
String viewInPlan;
if (LOGGER.isTraceEnabled()) {
viewInPlan = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
}
printPlan(pp, (AbstractLogicalOperator) op, "Original Whole plan1");
int phase = 1;
init(phase);
boolean canTransform = getJoinOpsAndLeafInputs(null, op, -1, phase);
if (!canTransform) {
return cleanUp();
}
if (everyLeafInputDoesNotHaveADataScanOperator(leafInputs)) {
return cleanUp();
}
if (LOGGER.isTraceEnabled()) {
viewInPlan = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
}
if (arrayUnnestPossible) {
joinEnum.stats = new Stats(context, joinEnum);
if (cboMode) {
if (!doAllDataSourcesHaveSamples(leafInputs, context)) {
return cleanUp();
}
}
// Here on, we expect that changes can be made to the incoming plan and that optimization will proceed
// without any hitch. Basically, we cannot go back now!!
// now that we know it is safe to proceed with unnesting array optimization, we will remove
// the unnestOps and related assign ops from the leafInputs and add them back later at the right places.
//select count (*) from KS1 x, x.uarr_i, x.zarr_i, KS2 y, y. earr_i where x.rand_n = y.rand_n;
//realInput 0 = true (KS1)
//realInput 1 = false
//realInput 2 = false
//realInput 3 = true (KS2)
//realInput 4 = false
//Note: The Unnesting code may move UNNEST Ops from the leafInputs higher up in the plan.
//The code is not designed to deal with UNNEST Ops that are not in the leafInputs.
int i = -1;
int j = -1;
for (List<List<ILogicalOperator>> l : unnestOpsInfo) {
i++;
if (realLeafInputs.get(i)) {
j++;
removeUnnestOpsFromLeafInputLevel1(leafInputs.get(j), l);
}
}
// now the plan should have no unnestOps and no related assigns
if (LOGGER.isTraceEnabled()) {
String viewOldPlan = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
}
introduceFakeOuterJoins(opRef, context);
if (LOGGER.isTraceEnabled()) {
String viewNewPlan = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
}
phase = 2;
init(phase);
getJoinOpsAndLeafInputs(null, op, -1, phase);
} else {
unnestOpsInfo.clear();
}
collectJoinConditionsVariables(); // will be used for determining which variables will be projected from the base levels
convertOuterJoinstoJoinsIfPossible(outerJoinsDependencyList);
printPlan(pp, (AbstractLogicalOperator) op, "Original Whole plan2");
numberOfFromTerms = leafInputs.size();
if (LOGGER.isTraceEnabled()) {
viewInPlan = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
LOGGER.trace("viewInPlan");
LOGGER.trace(viewInPlan);
}
if (buildSets.size() > 1) {
buildSets.sort(Comparator.comparingDouble(o -> o.second)); // sort on the number of tables in each set
// we need to build the smaller sets first. So we need to find these first.
}
joinEnum.initEnum((AbstractLogicalOperator) op, cboMode, cboTestMode, numberOfFromTerms, leafInputs, allJoinOps,
assignOps, outerJoinsDependencyList, buildSets, varLeafInputIds, unnestOpsInfo,
dataScanAndGroupByDistinctOps, rootGroupByDistinctOp, rootOrderByOp, resultAndJoinVars,
fakeLeafInputsMap, context);
if (cboMode) {
if (!doAllDataSourcesHaveSamples(leafInputs, context)) {
return cleanUp();
}
}
if (LOGGER.isTraceEnabled()) {
viewInPlan = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
}
printLeafPlans(pp, leafInputs, "Inputs1");
if (assignOps.size() > 0) {
pushAssignsIntoLeafInputs(pp, leafInputs, assignOps, assignJoinExprs);
}
if (LOGGER.isTraceEnabled()) {
viewInPlan = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
}
printLeafPlans(pp, leafInputs, "Inputs2");
if (LOGGER.isTraceEnabled()) {
String viewPlan = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
}
int cheapestPlan = joinEnum.enumerateJoins(); // MAIN CALL INTO CBO
if (cheapestPlan == PlanNode.NO_PLAN) {
return cleanUp();
}
PlanNode cheapestPlanNode = joinEnum.allPlans.get(cheapestPlan);
generateHintWarnings();
ILogicalOperator root = op;
if (numberOfFromTerms > 1) {
getNewJoinOps(cheapestPlanNode, allJoinOps);
if (allJoinOps.size() != newJoinOps.size()) {
return cleanUp(); // 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.
}
if (LOGGER.isTraceEnabled()) {
String viewInPlan2 = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
}
buildNewTree(cheapestPlanNode, newJoinOps, new MutableInt(0), context);
root = newJoinOps.get(0);
if (LOGGER.isTraceEnabled()) {
String viewInPlan2 = new ALogicalPlanImpl(new MutableObject<>(root)).toString();
}
if (phase == 2) {
// Now remove the Fake outer joins and put in the original Unnest Ops along with the corresponding Assign Ops
modifyUnnestInfo = new ArrayList<>();
collectUnnestModificationInfo(null, root, cheapestPlanNode);
for (int k = 0; k < modifyUnnestInfo.size(); k++) {
modifyTree(null, root, k);
if (newRootAfterUnnest != null) {
root = newRootAfterUnnest;
}
}
Mutable<ILogicalOperator> rootRef = new MutableObject<>(root);
if (LOGGER.isTraceEnabled()) {
String viewInPlan2 = new ALogicalPlanImpl(rootRef).toString(); //useful when debugging
}
}
opRef.setValue(root);
if (LOGGER.isTraceEnabled()) {
String viewInPlan2 = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
}
context.computeAndSetTypeEnvironmentForOperator(root);
if (assignOps.size() > 0) {
for (int i = assignOps.size() - 1; i >= 0; i--) {
MutableBoolean removed = new MutableBoolean(false);
removed.setFalse();
pushAssignsAboveJoins(root, assignOps.get(i), assignJoinExprs.get(i), removed);
context.computeAndSetTypeEnvironmentForOperator(assignOps.get(i));
if (removed.isTrue()) {
assignOps.remove(i);
}
}
}
printPlan(pp, (AbstractLogicalOperator) root, "New Whole Plan after buildNewTree 1");
root = addRemainingAssignsAtTheTop(root, assignOps);
printPlan(pp, (AbstractLogicalOperator) root, "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()) {
String viewOutPlan = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
LOGGER.trace("viewOutPlan");
LOGGER.trace(viewOutPlan);
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("---------------------------- Printing Leaf Inputs");
printLeafPlans(pp, leafInputs, "Inputs");
printPlan(pp, (AbstractLogicalOperator) root, "New Whole Plan");
}
// turn of this rule for all joins in this set (subtree)
for (ILogicalOperator joinOp : newJoinOps) {
context.addToDontApplySet(this, joinOp);
}
} else {
buildNewTree(cheapestPlanNode);
}
context.computeAndSetTypeEnvironmentForOperator(root);
if (LOGGER.isTraceEnabled()) {
String finalPlan = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
}
return true;
}