diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java index 1704c62904..c4b2945e7c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java @@ -82,6 +82,7 @@ import org.apache.doris.nereids.rules.rewrite.MergeFilters; import org.apache.doris.nereids.rules.rewrite.MergeOneRowRelationIntoUnion; import org.apache.doris.nereids.rules.rewrite.MergeProjects; import org.apache.doris.nereids.rules.rewrite.MergeSetOperations; +import org.apache.doris.nereids.rules.rewrite.MergeSetOperationsExcept; import org.apache.doris.nereids.rules.rewrite.MergeTopNs; import org.apache.doris.nereids.rules.rewrite.NormalizeSort; import org.apache.doris.nereids.rules.rewrite.OrExpansion; @@ -274,7 +275,7 @@ public class Rewriter extends AbstractBatchJobExecutor { topic("Set operation optimization", // Do MergeSetOperation first because we hope to match pattern of Distinct SetOperator. topDown(new PushProjectThroughUnion(), new MergeProjects()), - bottomUp(new MergeSetOperations()), + bottomUp(new MergeSetOperations(), new MergeSetOperationsExcept()), bottomUp(new PushProjectIntoOneRowRelation()), topDown(new MergeOneRowRelationIntoUnion()), topDown(new PushProjectIntoUnion()), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java index 54ddd4152f..248fec7de3 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java @@ -245,6 +245,7 @@ public enum RuleType { PUSH_PROJECT_INTO_ONE_ROW_RELATION(RuleTypeClass.REWRITE), PUSH_PROJECT_INTO_UNION(RuleTypeClass.REWRITE), MERGE_SET_OPERATION(RuleTypeClass.REWRITE), + MERGE_SET_OPERATION_EXCEPT(RuleTypeClass.REWRITE), MERGE_TOP_N(RuleTypeClass.REWRITE), BUILD_AGG_FOR_UNION(RuleTypeClass.REWRITE), COUNT_DISTINCT_REWRITE(RuleTypeClass.REWRITE), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeSetOperations.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeSetOperations.java index 152409d2b5..33d9d50aca 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeSetOperations.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeSetOperations.java @@ -22,6 +22,7 @@ import org.apache.doris.nereids.rules.RuleType; import org.apache.doris.nereids.trees.expressions.SlotReference; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.algebra.SetOperation.Qualifier; +import org.apache.doris.nereids.trees.plans.logical.LogicalExcept; import org.apache.doris.nereids.trees.plans.logical.LogicalSetOperation; import com.google.common.collect.ImmutableList; @@ -43,12 +44,22 @@ import java.util.List; * / | \ * scan1 scan2 scan3 * + * Notice: this rule ignore Except. + * Relational Algebra: Union (R U S), Intersect Syntax: (R ∩ S), Except Syntax: (R - S) + * TODO: Except need other Rewrite. + * + * + * + * + * */ -public class MergeSetOperations implements RewriteRuleFactory { +public class MergeSetOperations extends OneRewriteRuleFactory { @Override - public List buildRules() { - return ImmutableList.of( - logicalSetOperation(any(), any()).when(MergeSetOperations::canMerge).then(parentSetOperation -> { + public Rule build() { + return logicalSetOperation(any(), any()) + .whenNot(LogicalExcept.class::isInstance) + .when(MergeSetOperations::canMerge) + .then(parentSetOperation -> { ImmutableList.Builder newChildren = ImmutableList.builder(); ImmutableList.Builder> newChildrenOutputs = ImmutableList.builder(); for (int i = 0; i < parentSetOperation.arity(); i++) { @@ -64,8 +75,7 @@ public class MergeSetOperations implements RewriteRuleFactory { } return parentSetOperation.withChildrenAndTheirOutputs( newChildren.build(), newChildrenOutputs.build()); - }).toRule(RuleType.MERGE_SET_OPERATION) - ); + }).toRule(RuleType.MERGE_SET_OPERATION); } /** canMerge */ diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeSetOperationsExcept.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeSetOperationsExcept.java new file mode 100644 index 0000000000..2eb284e69c --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeSetOperationsExcept.java @@ -0,0 +1,64 @@ +// 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.SlotReference; +import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.logical.LogicalExcept; + +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * Merge left deep except + *
+ *     Except
+ *    /     \
+ *  Except  scan3
+ *   /    \
+ * scan1 scan2
+ * -->
+ *      Except
+ *     /  |  \
+ * scan1 scan2 scan3
+ * 
+ */ +public class MergeSetOperationsExcept extends OneRewriteRuleFactory { + @Override + public Rule build() { + return logicalExcept(logicalExcept(), any()) + .when(e -> e.getQualifier() == ((LogicalExcept) e.child(0)).getQualifier()) + .then(parentExcept -> { + LogicalExcept childExcept = (LogicalExcept) parentExcept.child(0); + + ImmutableList.Builder newChildren = ImmutableList.builder(); + ImmutableList.Builder> newChildrenOutputs = ImmutableList.builder(); + + newChildren.addAll(childExcept.children()); + newChildren.addAll(parentExcept.children().subList(1, parentExcept.children().size())); + newChildrenOutputs.addAll(childExcept.getRegularChildrenOutputs()); + newChildrenOutputs.addAll( + parentExcept.getRegularChildrenOutputs().subList(1, parentExcept.children().size())); + + return parentExcept.withChildrenAndTheirOutputs(newChildren.build(), newChildrenOutputs.build()); + }).toRule(RuleType.MERGE_SET_OPERATION_EXCEPT); + } +} diff --git a/regression-test/data/nereids_p0/set_operations/except.out b/regression-test/data/nereids_p0/set_operations/except.out new file mode 100644 index 0000000000..618df8d7ef --- /dev/null +++ b/regression-test/data/nereids_p0/set_operations/except.out @@ -0,0 +1,148 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !except_left_deep -- +1 a +2 b +3 c +4 d +5 e +6 f +7 g +8 h +9 i + +-- !except_left_deep_shape -- +PhysicalResultSink +--PhysicalExcept +----filter((t1.__DORIS_DELETE_SIGN__ = 0)) +------PhysicalOlapScan[t1] +----filter((t2.__DORIS_DELETE_SIGN__ = 0)) +------PhysicalOlapScan[t2] +----filter((t3.__DORIS_DELETE_SIGN__ = 0)) +------PhysicalOlapScan[t3] + +-- !except_right_deep -- +1 a +10 j +2 b +3 c +4 d +5 e +6 f +7 g +8 h +9 i + +-- !except_right_deep_shape -- +PhysicalResultSink +--PhysicalExcept +----filter((t1.__DORIS_DELETE_SIGN__ = 0)) +------PhysicalOlapScan[t1] +----PhysicalExcept +------filter((t2.__DORIS_DELETE_SIGN__ = 0)) +--------PhysicalOlapScan[t2] +------filter((t3.__DORIS_DELETE_SIGN__ = 0)) +--------PhysicalOlapScan[t3] + +-- !except_left_deep_right_deep -- +1 a +2 b +3 c +4 d +5 e +6 f +7 g +8 h +9 i + +-- !except_left_deep_right_deep_shape -- +PhysicalResultSink +--PhysicalExcept +----filter((t1.__DORIS_DELETE_SIGN__ = 0)) +------PhysicalOlapScan[t1] +----filter((t2.__DORIS_DELETE_SIGN__ = 0)) +------PhysicalOlapScan[t2] +----PhysicalExcept +------filter((t3.__DORIS_DELETE_SIGN__ = 0)) +--------PhysicalOlapScan[t3] +------filter((t2.__DORIS_DELETE_SIGN__ = 0)) +--------PhysicalOlapScan[t2] + +-- !except_left_deep -- +1 a +2 b +3 c +3 l +4 d +5 e +6 f +7 g +8 h +9 i +9 o + +-- !except_left_deep_shape -- +PhysicalResultSink +--PhysicalExcept +----hashAgg[GLOBAL] +------hashAgg[LOCAL] +--------PhysicalUnion +----------filter((t1.__DORIS_DELETE_SIGN__ = 0)) +------------PhysicalOlapScan[t1] +----------filter((t2.__DORIS_DELETE_SIGN__ = 0)) +------------PhysicalOlapScan[t2] +----filter((t3.__DORIS_DELETE_SIGN__ = 0)) +------PhysicalOlapScan[t3] + +-- !except_right_deep -- +1 a +2 b +3 c +4 d +5 e +6 f +7 g +8 h +9 i + +-- !except_right_deep_shape -- +PhysicalResultSink +--PhysicalExcept +----filter((t1.__DORIS_DELETE_SIGN__ = 0)) +------PhysicalOlapScan[t1] +----hashAgg[GLOBAL] +------hashAgg[LOCAL] +--------PhysicalUnion +----------filter((t2.__DORIS_DELETE_SIGN__ = 0)) +------------PhysicalOlapScan[t2] +----------filter((t3.__DORIS_DELETE_SIGN__ = 0)) +------------PhysicalOlapScan[t3] + +-- !except_left_deep_right_deep -- +1 a +2 b +3 c +4 d +5 e +6 f +7 g +8 h +9 i + +-- !except_left_deep_right_deep_shape -- +PhysicalResultSink +--PhysicalExcept +----hashAgg[GLOBAL] +------hashAgg[LOCAL] +--------PhysicalUnion +----------filter((t1.__DORIS_DELETE_SIGN__ = 0)) +------------PhysicalOlapScan[t1] +----------filter((t2.__DORIS_DELETE_SIGN__ = 0)) +------------PhysicalOlapScan[t2] +----hashAgg[GLOBAL] +------hashAgg[LOCAL] +--------PhysicalUnion +----------filter((t3.__DORIS_DELETE_SIGN__ = 0)) +------------PhysicalOlapScan[t3] +----------filter((t2.__DORIS_DELETE_SIGN__ = 0)) +------------PhysicalOlapScan[t2] + diff --git a/regression-test/suites/nereids_p0/set_operations/except.groovy b/regression-test/suites/nereids_p0/set_operations/except.groovy new file mode 100644 index 0000000000..522299aced --- /dev/null +++ b/regression-test/suites/nereids_p0/set_operations/except.groovy @@ -0,0 +1,126 @@ +// 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. + +suite("test_except", "query") { + sql "SET enable_nereids_planner=true" + sql "SET enable_fallback_to_original_planner=false" + sql "drop table if exists t1;" + sql "drop table if exists t2;" + sql "drop table if exists t3;" + sql "set ignore_shape_nodes='PhysicalDistribute,PhysicalProject'" + + sql ''' + CREATE TABLE t1 ( + a int NOT NULL, + b VARCHAR(25) NOT NULL, + )ENGINE=OLAP + unique KEY(`a`) + COMMENT "OLAP" + DISTRIBUTED BY HASH(`a`) BUCKETS 1 + PROPERTIES ( + "replication_num" = "1" + ); + ''' + sql ''' + CREATE TABLE t2 ( + a int NOT NULL, + b VARCHAR(25) NOT NULL, + )ENGINE=OLAP + unique KEY(`a`) + COMMENT "OLAP" + DISTRIBUTED BY HASH(`a`) BUCKETS 1 + PROPERTIES ( + "replication_num" = "1" + ); + ''' + sql ''' + CREATE TABLE t3 ( + a int NOT NULL, + b VARCHAR(25) NOT NULL, + )ENGINE=OLAP + unique KEY(`a`) + COMMENT "OLAP" + DISTRIBUTED BY HASH(`a`) BUCKETS 1 + PROPERTIES ( + "replication_num" = "1" + ); + ''' + + sql """ + insert into t1 values(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f'), (7, 'g'), (8, 'h'), (9, 'i'), (10, 'j'); + """ + sql """ + insert into t2 values(1, 'k'), (3, 'l'), (5, 'm'), (7, 'n'), (9, 'o'); + """ + sql """ + insert into t3 values(10, 'j'), (1, 'k'), (4, 'l'), (5, 'm'), (7, 'n'), (8, 'o'); + """ + + // (a except b) except c + order_qt_except_left_deep """ + select * from (select * from t1 except select * from t2) t except select * from t3; + """ + + qt_except_left_deep_shape """ + explain shape plan select * from (select * from t1 except select * from t2) t except select * from t3; + """ + + // a except (b except c) + order_qt_except_right_deep """ + select * from t1 except select * from (select * from t2 except select * from t3) t; + """ + + qt_except_right_deep_shape """ + explain shape plan select * from t1 except select * from (select * from t2 except select * from t3) t; + """ + + // (a except b) except (c except d) + order_qt_except_left_deep_right_deep """ + select * from (select * from t1 except select * from t2) t except select * from (select * from t3 except select * from t2) t; + """ + + qt_except_left_deep_right_deep_shape """ + explain shape plan select * from (select * from t1 except select * from t2) t except select * from (select * from t3 except select * from t2) t; + """ + + // (a union b) except c + order_qt_except_left_deep """ + select * from (select * from t1 union select * from t2) t except select * from t3; + """ + + qt_except_left_deep_shape """ + explain shape plan select * from (select * from t1 union select * from t2) t except select * from t3; + """ + + // a except (b union c) + order_qt_except_right_deep """ + select * from t1 except select * from (select * from t2 union select * from t3) t; + """ + + qt_except_right_deep_shape """ + explain shape plan select * from t1 except select * from (select * from t2 union select * from t3) t; + """ + + // (a union b) except (c union d) + order_qt_except_left_deep_right_deep """ + select * from (select * from t1 union select * from t2) t except select * from (select * from t3 union select * from t2) t; + """ + + qt_except_left_deep_right_deep_shape """ + explain shape plan select * from (select * from t1 union select * from t2) t except select * from (select * from t3 union select * from t2) t; + """ +}