in tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/rewrite/FilterPushDownRule.java [102:238]
public LogicalNode visitJoin(Set<EvalNode> cnf, LogicalPlan plan, LogicalPlan.QueryBlock block, JoinNode joinNode,
Stack<LogicalNode> stack) throws PlanningException {
LogicalNode left = joinNode.getRightChild();
LogicalNode right = joinNode.getLeftChild();
// here we should stop selection pushdown on the null supplying side(s) of an outer join
// get the two operands of the join operation as well as the join type
JoinType joinType = joinNode.getJoinType();
EvalNode joinQual = joinNode.getJoinQual();
if (joinQual != null && isOuterJoin(joinType)) {
// if both are fields
if (joinQual.getLeftExpr().getType() == EvalType.FIELD && joinQual.getRightExpr().getType() == EvalType.FIELD) {
String leftTableName = ((FieldEval) joinQual.getLeftExpr()).getQualifier();
String rightTableName = ((FieldEval) joinQual.getRightExpr()).getQualifier();
List<String> nullSuppliers = Lists.newArrayList();
Set<String> leftTableSet = Sets.newHashSet(PlannerUtil.getRelationLineageWithinQueryBlock(plan,
joinNode.getLeftChild()));
Set<String> rightTableSet = Sets.newHashSet(PlannerUtil.getRelationLineageWithinQueryBlock(plan,
joinNode.getRightChild()));
// some verification
if (joinType == JoinType.FULL_OUTER) {
nullSuppliers.add(leftTableName);
nullSuppliers.add(rightTableName);
// verify that these null suppliers are indeed in the left and right sets
if (!rightTableSet.contains(nullSuppliers.get(0)) && !leftTableSet.contains(nullSuppliers.get(0))) {
throw new InvalidQueryException("Incorrect Logical Query Plan with regard to outer join");
}
if (!rightTableSet.contains(nullSuppliers.get(1)) && !leftTableSet.contains(nullSuppliers.get(1))) {
throw new InvalidQueryException("Incorrect Logical Query Plan with regard to outer join");
}
} else if (joinType == JoinType.LEFT_OUTER) {
nullSuppliers.add(((RelationNode)joinNode.getRightChild()).getCanonicalName());
//verify that this null supplier is indeed in the right sub-tree
if (!rightTableSet.contains(nullSuppliers.get(0))) {
throw new InvalidQueryException("Incorrect Logical Query Plan with regard to outer join");
}
} else if (joinType == JoinType.RIGHT_OUTER) {
if (((RelationNode)joinNode.getRightChild()).getCanonicalName().equals(rightTableName)) {
nullSuppliers.add(leftTableName);
} else {
nullSuppliers.add(rightTableName);
}
// verify that this null supplier is indeed in the left sub-tree
if (!leftTableSet.contains(nullSuppliers.get(0))) {
throw new InvalidQueryException("Incorrect Logical Query Plan with regard to outer join");
}
}
// retain in this outer join node's JoinQual those selection predicates
// related to the outer join's null supplier(s)
List<EvalNode> matched2 = Lists.newArrayList();
for (EvalNode eval : cnf) {
Set<Column> columnRefs = EvalTreeUtil.findUniqueColumns(eval);
Set<String> tableNames = Sets.newHashSet();
// getting distinct table references
for (Column col : columnRefs) {
if (!tableNames.contains(col.getQualifier())) {
tableNames.add(col.getQualifier());
}
}
//if the predicate involves any of the null suppliers
boolean shouldKeep=false;
Iterator<String> it2 = nullSuppliers.iterator();
while(it2.hasNext()){
if(tableNames.contains(it2.next()) == true) {
shouldKeep = true;
}
}
if(shouldKeep == true) {
matched2.add(eval);
}
}
//merge the retained predicates and establish them in the current outer join node. Then remove them from the cnf
EvalNode qual2 = null;
if (matched2.size() > 1) {
// merged into one eval tree
qual2 = AlgebraicUtil.createSingletonExprFromCNF(
matched2.toArray(new EvalNode[matched2.size()]));
} else if (matched2.size() == 1) {
// if the number of matched expr is one
qual2 = matched2.get(0);
}
if (qual2 != null) {
EvalNode conjQual2 = AlgebraicUtil.createSingletonExprFromCNF(joinNode.getJoinQual(), qual2);
joinNode.setJoinQual(conjQual2);
cnf.removeAll(matched2);
} // for the remaining cnf, push it as usual
}
}
if (joinNode.hasJoinQual()) {
cnf.addAll(Sets.newHashSet(AlgebraicUtil.toConjunctiveNormalFormArray(joinNode.getJoinQual())));
}
visit(cnf, plan, block, left, stack);
visit(cnf, plan, block, right, stack);
List<EvalNode> matched = Lists.newArrayList();
for (EvalNode eval : cnf) {
if (LogicalPlanner.checkIfBeEvaluatedAtJoin(block, eval, joinNode, stack.peek().getType() != NodeType.JOIN)) {
matched.add(eval);
}
}
EvalNode qual = null;
if (matched.size() > 1) {
// merged into one eval tree
qual = AlgebraicUtil.createSingletonExprFromCNF(
matched.toArray(new EvalNode[matched.size()]));
} else if (matched.size() == 1) {
// if the number of matched expr is one
qual = matched.get(0);
}
if (qual != null) {
joinNode.setJoinQual(qual);
if (joinNode.getJoinType() == JoinType.CROSS) {
joinNode.setJoinType(JoinType.INNER);
}
cnf.removeAll(matched);
}
return joinNode;
}