use of io.crate.planner.operators.Filter in project crate by crate.
the class MoveFilterBeneathRename method apply.
@Override
public LogicalPlan apply(Filter plan, Captures captures, TableStats tableStats, TransactionContext txnCtx, NodeContext nodeCtx) {
Rename rename = captures.get(renameCapture);
Filter newFilter = new Filter(rename.source(), FieldReplacer.replaceFields(plan.query(), rename::resolveField));
return rename.replaceSources(List.of(newFilter));
}
use of io.crate.planner.operators.Filter in project crate by crate.
the class MoveFilterBeneathWindowAgg method apply.
@Override
public LogicalPlan apply(Filter filter, Captures captures, TableStats tableStats, TransactionContext txnCtx, NodeContext nodeCtx) {
WindowAgg windowAgg = captures.get(windowAggCapture);
WindowDefinition windowDefinition = windowAgg.windowDefinition();
List<WindowFunction> windowFunctions = windowAgg.windowFunctions();
Predicate<Symbol> containsWindowFunction = symbol -> symbol instanceof WindowFunction && windowFunctions.contains(symbol);
Symbol predicate = filter.query();
List<Symbol> filterParts = AndOperator.split(predicate);
ArrayList<Symbol> remainingFilterSymbols = new ArrayList<>();
ArrayList<Symbol> windowPartitionedBasedFilters = new ArrayList<>();
for (Symbol part : filterParts) {
if (SymbolVisitors.any(containsWindowFunction, part) == false && windowDefinition.partitions().containsAll(extractColumns(part))) {
windowPartitionedBasedFilters.add(part);
} else {
remainingFilterSymbols.add(part);
}
}
assert remainingFilterSymbols.size() > 0 || windowPartitionedBasedFilters.size() > 0 : "Splitting the filter symbol must result in at least one group";
/* SELECT ROW_NUMBER() OVER(PARTITION BY id)
* WHERE `x = 1`
*
* `x` is not the window partition column.
* We cannot push down the filter as it would change the window aggregation value
*
*/
if (windowPartitionedBasedFilters.isEmpty()) {
return null;
}
/* SELECT ROW_NUMBER() OVER(PARTITION BY id)
* WHERE `id = 1`
* remainingFilterSymbols: []
* windowPartitionedBasedFilters: [id = 1]
*
* Filter
* |
* WindowsAgg
*
* transforms to
*
* WindowAgg
* |
* Filter (id = 1)
*/
if (remainingFilterSymbols.isEmpty()) {
return transpose(filter, windowAgg);
}
/* WHERE `ROW_NUMBER() OVER(PARTITION BY id) = 2 AND id = 1`
* remainingFilterSymbols: [ROW_NUMBER() OVER(PARTITION BY id) = 2]
* windowPartitionedBasedFilters: [id = 1]
*
* Filter
* |
* WindowsAgg
*
* transforms to
*
* Filter (ROW_NUMBER() OVER(PARTITION BY id) = 2)
* |
* WindowAgg
* |
* Filter (id = 1)
*/
LogicalPlan newWindowAgg = windowAgg.replaceSources(List.of(new Filter(windowAgg.source(), AndOperator.join(windowPartitionedBasedFilters))));
return new Filter(newWindowAgg, AndOperator.join(remainingFilterSymbols));
}
use of io.crate.planner.operators.Filter 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.Filter in project crate by crate.
the class FilterOnJoinsUtil method moveQueryBelowJoin.
static LogicalPlan moveQueryBelowJoin(Symbol query, LogicalPlan join) {
if (!WhereClause.canMatch(query)) {
return join.replaceSources(List.of(getNewSource(query, join.sources().get(0)), getNewSource(query, join.sources().get(1))));
}
Map<Set<RelationName>, Symbol> splitQuery = QuerySplitter.split(query);
int initialParts = splitQuery.size();
if (splitQuery.size() == 1 && splitQuery.keySet().iterator().next().size() > 1) {
return null;
}
assert join.sources().size() == 2 : "Join operator must only have 2 children, LHS and RHS";
LogicalPlan lhs = join.sources().get(0);
LogicalPlan rhs = join.sources().get(1);
Set<RelationName> leftName = lhs.getRelationNames();
Set<RelationName> rightName = rhs.getRelationNames();
Symbol queryForLhs = splitQuery.remove(leftName);
Symbol queryForRhs = splitQuery.remove(rightName);
LogicalPlan newLhs = getNewSource(queryForLhs, lhs);
LogicalPlan newRhs = getNewSource(queryForRhs, rhs);
LogicalPlan newJoin = join.replaceSources(List.of(newLhs, newRhs));
if (splitQuery.isEmpty()) {
return newJoin;
} else if (initialParts == splitQuery.size()) {
return null;
} else {
return new Filter(newJoin, AndOperator.join(splitQuery.values()));
}
}
use of io.crate.planner.operators.Filter in project crate by crate.
the class CapturePatternTest method testCapturePatternAddsToCaptureOnMatch.
@Test
public void testCapturePatternAddsToCaptureOnMatch() {
Filter filter = new Filter(mock(LogicalPlan.class), Literal.BOOLEAN_TRUE);
Capture<Filter> captureFilter = new Capture<>();
Pattern<Filter> filterPattern = Pattern.typeOf(Filter.class).capturedAs(captureFilter);
CapturePattern<Filter> pattern = new CapturePattern<>(captureFilter, filterPattern);
Match<Filter> match = pattern.accept(filter, Captures.empty());
Filter capturedFilter = match.captures().get(captureFilter);
assertThat(capturedFilter, Matchers.sameInstance(filter));
}
Aggregations