[fix](nereids)EliminateSemiJoin should consider empty table (#32107)

* [fix](nereids)EliminateSemiJoin should consider empty table

* update out file
This commit is contained in:
starocean999
2024-03-13 13:46:20 +08:00
committed by yiguolei
parent c8c6e86386
commit 3c4234111b
3 changed files with 96 additions and 41 deletions

View File

@ -22,7 +22,6 @@ 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 org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
@ -35,40 +34,43 @@ public class EliminateSemiJoin extends OneRewriteRuleFactory {
@Override
public Rule build() {
return logicalJoin()
// right will be converted to left
.whenNot(LogicalJoin::isMarkJoin)
.when(join -> join.getJoinType().isLeftSemiOrAntiJoin())
.when(join -> join.getJoinType().isSemiOrAntiJoin())
.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;
if (otherJoinConjuncts.size() == 1
&& otherJoinConjuncts.get(0).equals(BooleanLiteral.FALSE)) {
switch (join.getJoinType()) {
case LEFT_SEMI_JOIN:
case RIGHT_SEMI_JOIN: {
return new LogicalEmptyRelation(
StatementScopeIdGenerator.newRelationId(),
join.getOutput());
}
case NULL_AWARE_LEFT_ANTI_JOIN:
case LEFT_ANTI_JOIN: {
return join.left();
}
case RIGHT_ANTI_JOIN: {
return join.right();
}
default:
throw new IllegalStateException("Unexpected join type: " + join.getJoinType());
}
} else {
/*
* A left semi join B on true, normally should output all rows from A
* A left anti join B on true, normally should output empty set
* but things are different if other side table is empty, examples:
* A left semi join B on true, if B is empty, should output empty set
* A left anti join B on true, if B is empty, should output all rows from A
* A right semi join B on true, if A is empty, should output empty set
* A right anti join B on true, if A is empty, should output all rows from B
* we can see even condition is true, the result depends on if other side table is empty
*/
return null;
}
if (joinType == JoinType.LEFT_SEMI_JOIN && condition) {
// the right table may be empty, we need keep plan unchanged
return null;
} else if (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);
}).toRule(RuleType.ELIMINATE_SEMI_JOIN);
}
}