use of io.crate.planner.operators.NestedLoopJoin in project crate by crate.
the class RewriteFilterOnOuterJoinToInnerJoin method apply.
@Override
public LogicalPlan apply(Filter filter, Captures captures, TableStats tableStats, TransactionContext txnCtx, NodeContext nodeCtx) {
var symbolEvaluator = new SymbolEvaluator(txnCtx, nodeCtx, SubQueryResults.EMPTY);
NestedLoopJoin nl = captures.get(nlCapture);
Symbol query = filter.query();
Map<Set<RelationName>, Symbol> splitQueries = QuerySplitter.split(query);
if (splitQueries.size() == 1 && splitQueries.keySet().iterator().next().size() > 1) {
return null;
}
LogicalPlan lhs = nl.sources().get(0);
LogicalPlan rhs = nl.sources().get(1);
Set<RelationName> leftName = lhs.getRelationNames();
Set<RelationName> rightName = rhs.getRelationNames();
Symbol leftQuery = splitQueries.remove(leftName);
Symbol rightQuery = splitQueries.remove(rightName);
final LogicalPlan newLhs;
final LogicalPlan newRhs;
final boolean newJoinIsInnerJoin;
switch(nl.joinType()) {
case LEFT:
/* LEFT OUTER JOIN -> NULL rows are generated for the RHS if the join-condition doesn't match
*
* cr> select t1.x as t1x, t2.x as t2x from t1 left join t2 on t1.x = t2.x;
* +-----+------+
* | t1x | t2x |
* +-----+------+
* | 3 | 3 |
* | 2 | 2 |
* | 1 | NULL |
* +-----+------+
*/
newLhs = getNewSource(leftQuery, lhs);
if (rightQuery == null) {
newRhs = rhs;
newJoinIsInnerJoin = false;
} else if (couldMatchOnNull(rightQuery, symbolEvaluator)) {
newRhs = rhs;
newJoinIsInnerJoin = false;
splitQueries.put(rightName, rightQuery);
} else {
newRhs = getNewSource(rightQuery, rhs);
newJoinIsInnerJoin = true;
}
break;
case RIGHT:
/* RIGHT OUTER JOIN -> NULL rows are generated for the LHS if the join-condition doesn't match
* cr> select t1.x as t1x, t2.x as t2x from t1 right join t2 on t1.x = t2.x;
* +------+-----+
* | t1x | t2x |
* +------+-----+
* | 3 | 3 |
* | 2 | 2 |
* | NULL | 4 |
* +------+-----+
*/
if (leftQuery == null) {
newLhs = lhs;
newJoinIsInnerJoin = false;
} else if (couldMatchOnNull(leftQuery, symbolEvaluator)) {
newLhs = lhs;
newJoinIsInnerJoin = false;
splitQueries.put(leftName, leftQuery);
} else {
newLhs = getNewSource(leftQuery, lhs);
newJoinIsInnerJoin = true;
}
newRhs = getNewSource(rightQuery, rhs);
break;
case FULL:
if (couldMatchOnNull(leftQuery, symbolEvaluator)) {
newLhs = lhs;
} else {
newLhs = getNewSource(leftQuery, lhs);
if (leftQuery != null) {
splitQueries.put(leftName, leftQuery);
}
}
if (couldMatchOnNull(rightQuery, symbolEvaluator)) {
newRhs = rhs;
} else {
newRhs = getNewSource(rightQuery, rhs);
}
/*
* Filters on each side must be put back into the Filter as each side can generate NULL's on outer joins
* which must be filtered out AFTER the join operation.
* In case the filter is only on one side, the join could be rewritten to a LEFT/RIGHT OUTER.
* TODO: Create a dedicated rule RewriteFilterOnOuterJoinToLeftOrRight
*
* cr> select t1.x as t1x, t2.x as t2x, t2.y as t2y from t1 full outer join t2 on t1.x = t2.x where t2y = 1;
* +------+------+------+
* | t1x | t2x | t2y |
* +------+------+------+
* | 3 | 3 | 1 |
* | 2 | 2 | 1 |
* | NULL | 4 | 1 |
* +------+------+------+
*/
if (leftQuery != null) {
splitQueries.put(leftName, leftQuery);
}
if (rightQuery != null) {
splitQueries.put(rightName, rightQuery);
}
newJoinIsInnerJoin = newLhs != lhs && newRhs != rhs;
break;
default:
throw new UnsupportedOperationException("The Rule to rewrite filter+outer-joins to inner joins must not be run on joins of type=" + nl.joinType());
}
if (newLhs == lhs && newRhs == rhs) {
return null;
}
NestedLoopJoin newJoin = new NestedLoopJoin(newLhs, newRhs, newJoinIsInnerJoin ? JoinType.INNER : nl.joinType(), nl.joinCondition(), nl.isFiltered(), nl.topMostLeftRelation(), nl.orderByWasPushedDown(), true);
assert newJoin.outputs().equals(nl.outputs()) : "Outputs after rewrite must be the same as before";
return splitQueries.isEmpty() ? newJoin : new Filter(newJoin, AndOperator.join(splitQueries.values()));
}
use of io.crate.planner.operators.NestedLoopJoin in project crate by crate.
the class MoveOrderBeneathNestedLoop method apply.
@Override
public LogicalPlan apply(Order order, Captures captures, TableStats tableStats, TransactionContext txnCtx, NodeContext nodeCtx) {
NestedLoopJoin nestedLoop = captures.get(nlCapture);
Set<RelationName> relationsInOrderBy = Collections.newSetFromMap(new IdentityHashMap<>());
Consumer<ScopedSymbol> gatherRelationsFromField = f -> relationsInOrderBy.add(f.relation());
Consumer<Reference> gatherRelationsFromRef = r -> relationsInOrderBy.add(r.ident().tableIdent());
OrderBy orderBy = order.orderBy();
for (Symbol orderExpr : orderBy.orderBySymbols()) {
FieldsVisitor.visitFields(orderExpr, gatherRelationsFromField);
RefVisitor.visitRefs(orderExpr, gatherRelationsFromRef);
}
if (relationsInOrderBy.size() == 1) {
RelationName relationInOrderBy = relationsInOrderBy.iterator().next();
if (relationInOrderBy == nestedLoop.topMostLeftRelation().relationName()) {
LogicalPlan lhs = nestedLoop.sources().get(0);
LogicalPlan newLhs = order.replaceSources(List.of(lhs));
return new NestedLoopJoin(newLhs, nestedLoop.sources().get(1), nestedLoop.joinType(), nestedLoop.joinCondition(), nestedLoop.isFiltered(), nestedLoop.topMostLeftRelation(), true, nestedLoop.isRewriteFilterOnOuterJoinToInnerJoinDone());
}
}
return null;
}
Aggregations