use of io.crate.analyze.SymbolEvaluator 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.analyze.SymbolEvaluator in project crate by crate.
the class RewriteFilterOnOuterJoinToInnerJoin method couldMatchOnNull.
private static boolean couldMatchOnNull(@Nullable Symbol query, SymbolEvaluator evaluator) {
if (query == null) {
return false;
}
Symbol queryWithNulls = RefReplacer.replaceRefs(FieldReplacer.replaceFields(query, ignored -> Literal.NULL), ignored -> Literal.NULL);
Input<?> input = queryWithNulls.accept(evaluator, ALL_NULL_ROW);
return WhereClause.canMatch(input);
}
use of io.crate.analyze.SymbolEvaluator in project crate by crate.
the class InsertFromValues method evaluateValueTableFunction.
private static Iterator<Row> evaluateValueTableFunction(TableFunctionImplementation<?> funcImplementation, List<Symbol> arguments, List<Reference> allTargetReferences, DocTableInfo tableInfo, Row params, PlannerContext plannerContext, SubQueryResults subQueryResults) {
SymbolEvaluator symbolEval = new SymbolEvaluator(plannerContext.transactionContext(), plannerContext.nodeContext(), subQueryResults);
Function<? super Symbol, Input<?>> eval = (symbol) -> symbol.accept(symbolEval, params);
ArrayList<Input<?>> boundArguments = new ArrayList<>(arguments.size());
for (int i = 0; i < arguments.size(); i++) {
boundArguments.add(eval.apply(arguments.get(i)));
}
// noinspection unchecked
Iterable<Row> rows = funcImplementation.evaluate(plannerContext.transactionContext(), plannerContext.nodeContext(), boundArguments.toArray(new Input[0]));
return StreamSupport.stream(rows.spliterator(), false).map(row -> cast(row, allTargetReferences, tableInfo)).iterator();
}
Aggregations