[feature](Nereids): eliminate semi join (#28588)
Eliminate Semi/Anti Join which is FALSE or TRUE.
This commit is contained in:
@ -60,6 +60,7 @@ import org.apache.doris.nereids.rules.rewrite.EliminateLimit;
|
||||
import org.apache.doris.nereids.rules.rewrite.EliminateNotNull;
|
||||
import org.apache.doris.nereids.rules.rewrite.EliminateNullAwareLeftAntiJoin;
|
||||
import org.apache.doris.nereids.rules.rewrite.EliminateOrderByConstant;
|
||||
import org.apache.doris.nereids.rules.rewrite.EliminateSemiJoin;
|
||||
import org.apache.doris.nereids.rules.rewrite.EliminateSort;
|
||||
import org.apache.doris.nereids.rules.rewrite.EliminateSortUnderSubquery;
|
||||
import org.apache.doris.nereids.rules.rewrite.EliminateUnnecessaryProject;
|
||||
@ -184,7 +185,8 @@ public class Rewriter extends AbstractBatchJobExecutor {
|
||||
new EliminateFilter(),
|
||||
new EliminateAggregate(),
|
||||
new EliminateJoinCondition(),
|
||||
new EliminateAssertNumRows()
|
||||
new EliminateAssertNumRows(),
|
||||
new EliminateSemiJoin()
|
||||
)
|
||||
),
|
||||
// please note: this rule must run before NormalizeAggregate
|
||||
|
||||
@ -202,6 +202,7 @@ public enum RuleType {
|
||||
ELIMINATE_JOIN(RuleTypeClass.REWRITE),
|
||||
ELIMINATE_JOIN_CONDITION(RuleTypeClass.REWRITE),
|
||||
ELIMINATE_FILTER_ON_ONE_RELATION(RuleTypeClass.REWRITE),
|
||||
ELIMINATE_SEMI_JOIN(RuleTypeClass.REWRITE),
|
||||
ELIMINATE_NOT_NULL(RuleTypeClass.REWRITE),
|
||||
ELIMINATE_UNNECESSARY_PROJECT(RuleTypeClass.REWRITE),
|
||||
ELIMINATE_OUTER_JOIN(RuleTypeClass.REWRITE),
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.rewrite;
|
||||
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.RuleType;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.StatementScopeIdGenerator;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
|
||||
import org.apache.doris.nereids.trees.plans.JoinType;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Eliminate Semi/Anti Join which is FALSE or TRUE.
|
||||
*/
|
||||
public class EliminateSemiJoin extends OneRewriteRuleFactory {
|
||||
@Override
|
||||
public Rule build() {
|
||||
return logicalJoin()
|
||||
// right will be converted to left
|
||||
.when(join -> join.getJoinType().isLeftSemiOrAntiJoin())
|
||||
.when(join -> join.getHashJoinConjuncts().isEmpty())
|
||||
.then(join -> {
|
||||
List<Expression> otherJoinConjuncts = join.getOtherJoinConjuncts();
|
||||
JoinType joinType = join.getJoinType();
|
||||
|
||||
boolean condition;
|
||||
if (otherJoinConjuncts.isEmpty()) {
|
||||
condition = true;
|
||||
} else if (otherJoinConjuncts.size() == 1) {
|
||||
if (otherJoinConjuncts.get(0).equals(BooleanLiteral.TRUE)) {
|
||||
condition = true;
|
||||
} else if (otherJoinConjuncts.get(0).equals(BooleanLiteral.FALSE)) {
|
||||
condition = false;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (joinType == JoinType.LEFT_SEMI_JOIN && condition
|
||||
|| (joinType == JoinType.LEFT_ANTI_JOIN && !condition)) {
|
||||
return join.left();
|
||||
} else if (joinType == JoinType.LEFT_SEMI_JOIN && !condition
|
||||
|| (joinType == JoinType.LEFT_ANTI_JOIN && condition)) {
|
||||
return new LogicalEmptyRelation(StatementScopeIdGenerator.newRelationId(), join.getOutput());
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected join type: " + joinType);
|
||||
}
|
||||
})
|
||||
.toRule(RuleType.ELIMINATE_SEMI_JOIN);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.rewrite;
|
||||
|
||||
import org.apache.doris.nereids.util.MemoPatternMatchSupported;
|
||||
import org.apache.doris.nereids.util.PlanChecker;
|
||||
import org.apache.doris.utframe.TestWithFeService;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class EliminateSemiJoinTest extends TestWithFeService implements MemoPatternMatchSupported {
|
||||
|
||||
@Override
|
||||
protected void runBeforeAll() throws Exception {
|
||||
createDatabase("test");
|
||||
|
||||
connectContext.setDatabase("test");
|
||||
|
||||
createTable("CREATE TABLE t ("
|
||||
+ "id int not null"
|
||||
+ ")\n"
|
||||
+ "DISTRIBUTED BY HASH(id)\n"
|
||||
+ "BUCKETS 1\n"
|
||||
+ "PROPERTIES(\n"
|
||||
+ " \"replication_num\"=\"1\"\n"
|
||||
+ ");");
|
||||
}
|
||||
|
||||
@Test
|
||||
void semiTrue() {
|
||||
String sql = "select * from t t1 left semi join t t2 on true";
|
||||
|
||||
PlanChecker.from(connectContext)
|
||||
.analyze(sql)
|
||||
.rewrite()
|
||||
.matches(
|
||||
logicalResultSink(
|
||||
logicalOlapScan()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void semiFalse() {
|
||||
String sql = "select * from t t1 left semi join t t2 on false";
|
||||
|
||||
PlanChecker.from(connectContext)
|
||||
.analyze(sql)
|
||||
.rewrite()
|
||||
.matches(
|
||||
logicalEmptyRelation()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void antiTrue() {
|
||||
String sql = "select * from t t1 left anti join t t2 on true";
|
||||
|
||||
PlanChecker.from(connectContext)
|
||||
.analyze(sql)
|
||||
.rewrite()
|
||||
.matches(
|
||||
logicalEmptyRelation()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void antiFalse() {
|
||||
String sql = "select * from t t1 left anti join t t2 on false";
|
||||
|
||||
PlanChecker.from(connectContext)
|
||||
.analyze(sql)
|
||||
.rewrite()
|
||||
.matches(
|
||||
logicalResultSink(
|
||||
logicalOlapScan()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user