This is an automated email from the ASF dual-hosted git repository. morningman pushed a commit to branch branch-1.2-lts in repository https://gitbox.apache.org/repos/asf/doris.git
commit e7589b83675f37092684af8b7b793e521d872477 Author: minghong <minghong.z...@163.com> AuthorDate: Tue Dec 20 09:43:34 2022 +0800 [feature](planner) compact multi-euqals to in-predicate #14876 --- .../java/org/apache/doris/analysis/Analyzer.java | 2 + .../rewrite/CompactEqualsToInPredicateRule.java | 160 +++++++++++++++++++++ .../doris/analysis/PartitionPruneTestBase.java | 2 - .../CompactEqualsToInPredicateRuleTest.java | 116 +++++++++++++++ .../data/performance_p0/redundant_conjuncts.out | 2 +- 5 files changed, 279 insertions(+), 3 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java index a34146c607..85289d9044 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java @@ -46,6 +46,7 @@ import org.apache.doris.planner.PlanNode; import org.apache.doris.planner.RuntimeFilter; import org.apache.doris.qe.ConnectContext; import org.apache.doris.rewrite.BetweenToCompoundRule; +import org.apache.doris.rewrite.CompactEqualsToInPredicateRule; import org.apache.doris.rewrite.CompoundPredicateWriteRule; import org.apache.doris.rewrite.ExprRewriteRule; import org.apache.doris.rewrite.ExprRewriter; @@ -410,6 +411,7 @@ public class Analyzer { rules.add(RewriteEncryptKeyRule.INSTANCE); rules.add(RewriteInPredicateRule.INSTANCE); rules.add(RewriteAliasFunctionRule.INSTANCE); + rules.add(CompactEqualsToInPredicateRule.INSTANCE); List<ExprRewriteRule> onceRules = Lists.newArrayList(); onceRules.add(ExtractCommonFactorsRule.INSTANCE); onceRules.add(InferFiltersRule.INSTANCE); diff --git a/fe/fe-core/src/main/java/org/apache/doris/rewrite/CompactEqualsToInPredicateRule.java b/fe/fe-core/src/main/java/org/apache/doris/rewrite/CompactEqualsToInPredicateRule.java new file mode 100644 index 0000000000..7375b83121 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/rewrite/CompactEqualsToInPredicateRule.java @@ -0,0 +1,160 @@ +// 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.rewrite; + +import org.apache.doris.analysis.Analyzer; +import org.apache.doris.analysis.BinaryPredicate; +import org.apache.doris.analysis.CompoundPredicate; +import org.apache.doris.analysis.CompoundPredicate.Operator; +import org.apache.doris.analysis.Expr; +import org.apache.doris.analysis.InPredicate; +import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.analysis.SlotRef; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Pair; +import org.apache.doris.rewrite.ExprRewriter.ClauseType; + +import com.google.common.collect.Lists; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/* +a = 1 or a = 2 or a = 3 or a in (4, 5, 6) => a in (1, 2, 3, 4, 5, 6) + */ +public class CompactEqualsToInPredicateRule implements ExprRewriteRule { + public static CompactEqualsToInPredicateRule INSTANCE = new CompactEqualsToInPredicateRule(); + private static final int COMPACT_SIZE = 2; + + @Override + public Expr apply(Expr expr, Analyzer analyzer, ClauseType clauseType) throws AnalysisException { + if (expr == null) { + return expr; + } + if (expr instanceof CompoundPredicate) { + CompoundPredicate comp = (CompoundPredicate) expr; + if (comp.getOp() == Operator.OR) { + Pair<Boolean, Expr> compactResult = compactEqualsToInPredicate(expr); + if (compactResult.first) { + return compactResult.second; + } + } + } + return expr; + } + + /* + expr in form of A or B or ... + */ + private Pair<Boolean, Expr> compactEqualsToInPredicate(Expr expr) { + boolean changed = false; + List<Expr> disConjuncts = getDisconjuncts(expr); + if (disConjuncts.size() < COMPACT_SIZE) { + return Pair.of(false, expr); + } + Map<SlotRef, Set<Expr>> equalMap = new HashMap<>(); + Map<SlotRef, InPredicate> inPredMap = new HashMap<>(); + Expr result = null; + for (Expr disConj : disConjuncts) { + if (disConj instanceof BinaryPredicate + && ((BinaryPredicate) disConj).getOp() == BinaryPredicate.Operator.EQ) { + BinaryPredicate binary = (BinaryPredicate) disConj; + if (binary.getChild(0) instanceof SlotRef + && binary.getChild(1) instanceof LiteralExpr) { + equalMap.computeIfAbsent((SlotRef) binary.getChild(0), k -> new HashSet<>()); + equalMap.get((SlotRef) binary.getChild(0)).add((LiteralExpr) binary.getChild(1)); + } else if (binary.getChild(0) instanceof LiteralExpr + && binary.getChild(1) instanceof SlotRef) { + equalMap.computeIfAbsent((SlotRef) binary.getChild(1), k -> new HashSet<>()); + equalMap.get((SlotRef) binary.getChild(1)).add((LiteralExpr) binary.getChild(0)); + } else { + result = addDisconjunct(result, disConj); + } + } else if (disConj instanceof InPredicate && !((InPredicate) disConj).isNotIn()) { + InPredicate in = (InPredicate) disConj; + Expr compareExpr = in.getChild(0); + if (compareExpr instanceof SlotRef) { + SlotRef slot = (SlotRef) compareExpr; + InPredicate val = inPredMap.get(slot); + if (val == null) { + inPredMap.put(slot, in); + } else { + val.getChildren().addAll(in.getListChildren()); + inPredMap.put(slot, val); + } + } + } else { + result = addDisconjunct(result, disConj); + } + } + + for (Entry<SlotRef, Set<Expr>> entry : equalMap.entrySet()) { + SlotRef slot = entry.getKey(); + InPredicate in = inPredMap.get(slot); + if (entry.getValue().size() >= COMPACT_SIZE || in != null) { + if (in == null) { + in = new InPredicate(entry.getKey(), Lists.newArrayList(entry.getValue()), false); + inPredMap.put(slot, in); + } else { + in.getChildren().addAll(Lists.newArrayList(entry.getValue())); + } + changed = true; + } else { + for (Expr right : entry.getValue()) { + result = addDisconjunct(result, + new BinaryPredicate(BinaryPredicate.Operator.EQ, + entry.getKey(), + right)); + } + } + } + for (InPredicate in : inPredMap.values()) { + result = addDisconjunct(result, in); + } + return Pair.of(changed, result); + } + + private Expr addDisconjunct(Expr result, Expr conj) { + if (result == null) { + return conj; + } else { + return new CompoundPredicate(Operator.OR, result, conj); + } + } + + private List<Expr> getDisconjuncts(Expr expr) { + List<Expr> result = new ArrayList<>(); + if (expr instanceof CompoundPredicate) { + CompoundPredicate comp = ((CompoundPredicate) expr); + if (comp.getOp() == Operator.OR) { + result.addAll(getDisconjuncts(comp.getChild(0))); + result.addAll(getDisconjuncts(comp.getChild(1))); + } else { + result.add(expr); + } + } else { + result.add(expr); + } + return result; + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/PartitionPruneTestBase.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/PartitionPruneTestBase.java index 95c4790bea..7ff8e2fda1 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/analysis/PartitionPruneTestBase.java +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/PartitionPruneTestBase.java @@ -29,8 +29,6 @@ public abstract class PartitionPruneTestBase extends TestWithFeService { protected void doTest() throws Exception { for (RangePartitionPruneTest.TestCase testCase : cases) { - connectContext.getSessionVariable().partitionPruneAlgorithmVersion = 1; - assertExplainContains(1, testCase.sql, testCase.v1Result); connectContext.getSessionVariable().partitionPruneAlgorithmVersion = 2; assertExplainContains(2, testCase.sql, testCase.v2Result); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/rewrite/CompactEqualsToInPredicateRuleTest.java b/fe/fe-core/src/test/java/org/apache/doris/rewrite/CompactEqualsToInPredicateRuleTest.java new file mode 100644 index 0000000000..baa694ea42 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/rewrite/CompactEqualsToInPredicateRuleTest.java @@ -0,0 +1,116 @@ +// 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.rewrite; + +import org.apache.doris.analysis.BinaryPredicate; +import org.apache.doris.analysis.CompoundPredicate; +import org.apache.doris.analysis.CompoundPredicate.Operator; +import org.apache.doris.analysis.InPredicate; +import org.apache.doris.analysis.IntLiteral; +import org.apache.doris.analysis.SlotRef; +import org.apache.doris.analysis.TableName; +import org.apache.doris.common.Pair; +import org.apache.doris.common.jmockit.Deencapsulation; +import org.apache.doris.datasource.InternalCatalog; + +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; + +public class CompactEqualsToInPredicateRuleTest { + private static final String internalCtl = InternalCatalog.INTERNAL_CATALOG_NAME; + + //a=1 or b=2 or a=3 or b=4 + //=> a in (1, 2) or b in (3, 4) + @Test + public void testCompactEquals() { + SlotRef a = new SlotRef(new TableName(internalCtl, "db1", "tb1"), "a"); + SlotRef b = new SlotRef(new TableName(internalCtl, "db1", "tb1"), "b"); + IntLiteral i1 = new IntLiteral(1); + IntLiteral i2 = new IntLiteral(2); + IntLiteral i3 = new IntLiteral(3); + IntLiteral i4 = new IntLiteral(4); + BinaryPredicate aeq1 = new BinaryPredicate(BinaryPredicate.Operator.EQ, i1, a); + BinaryPredicate aeq3 = new BinaryPredicate(BinaryPredicate.Operator.EQ, a, i3); + BinaryPredicate beq2 = new BinaryPredicate(BinaryPredicate.Operator.EQ, b, i2); + BinaryPredicate beq4 = new BinaryPredicate(BinaryPredicate.Operator.EQ, b, i4); + CompoundPredicate or1 = new CompoundPredicate(Operator.OR, aeq1, beq2); + CompoundPredicate or2 = new CompoundPredicate(Operator.OR, or1, aeq3); + CompoundPredicate or3 = new CompoundPredicate(Operator.OR, or2, beq4); + CompactEqualsToInPredicateRule rule = new CompactEqualsToInPredicateRule(); + Pair result = Deencapsulation.invoke(rule, + "compactEqualsToInPredicate", or3); + Assertions.assertEquals(true, result.first); + Assertions.assertTrue(result.second instanceof CompoundPredicate); + CompoundPredicate or = (CompoundPredicate) result.second; + Assertions.assertEquals(Operator.OR, or.getOp()); + InPredicate in1 = (InPredicate) or.getChild(0); + InPredicate in2 = (InPredicate) or.getChild(1); + SlotRef s1 = (SlotRef) in1.getChildren().get(0); + InPredicate tmp; + if (s1.getColumnName().equals("b")) { + tmp = in1; + in1 = in2; + in2 = tmp; + } + Assertions.assertEquals(in1.getChild(0), a); + Assertions.assertEquals(in2.getChild(0), b); + + HashSet<IntLiteral> seta = new HashSet<>(); + seta.add(i1); + seta.add(i3); + HashSet<IntLiteral> setb = new HashSet<>(); + setb.add(i2); + setb.add(i4); + + Assertions.assertTrue(seta.contains(in1.getChild(1))); + Assertions.assertTrue(seta.contains(in1.getChild(2))); + + Assertions.assertTrue(setb.contains(in2.getChild(1))); + Assertions.assertTrue(setb.contains(in2.getChild(2))); + } + + //a=1 or a in (3, 2) => a in (1, 2, 3) + @Test + public void testCompactEqualsAndIn() { + SlotRef a = new SlotRef(new TableName(internalCtl, "db1", "tb1"), "a"); + IntLiteral i1 = new IntLiteral(1); + IntLiteral i2 = new IntLiteral(2); + IntLiteral i3 = new IntLiteral(3); + BinaryPredicate aeq1 = new BinaryPredicate(BinaryPredicate.Operator.EQ, i1, a); + InPredicate ain23 = new InPredicate(a, Lists.newArrayList(i2, i3), false); + CompoundPredicate or1 = new CompoundPredicate(Operator.OR, aeq1, ain23); + CompactEqualsToInPredicateRule rule = new CompactEqualsToInPredicateRule(); + Pair result = Deencapsulation.invoke(rule, + "compactEqualsToInPredicate", or1); + Assertions.assertEquals(true, result.first); + Assertions.assertTrue(result.second instanceof InPredicate); + InPredicate in123 = (InPredicate) result.second; + Assertions.assertEquals(in123.getChild(0), a); + HashSet<IntLiteral> seta = new HashSet<>(); + seta.add(i1); + seta.add(i2); + seta.add(i3); + + Assertions.assertTrue(seta.contains(in123.getChild(1))); + Assertions.assertTrue(seta.contains(in123.getChild(2))); + Assertions.assertTrue(seta.contains(in123.getChild(3))); + } +} diff --git a/regression-test/data/performance_p0/redundant_conjuncts.out b/regression-test/data/performance_p0/redundant_conjuncts.out index 98178f31aa..9cba503956 100644 --- a/regression-test/data/performance_p0/redundant_conjuncts.out +++ b/regression-test/data/performance_p0/redundant_conjuncts.out @@ -23,7 +23,7 @@ PLAN FRAGMENT 0 0:VOlapScanNode TABLE: default_cluster:regression_test_performance_p0.redundant_conjuncts(redundant_conjuncts), PREAGGREGATION: OFF. Reason: No AggregateInfo - PREDICATES: (`k1` = 1 OR `k1` = 2) + PREDICATES: `k1` IN (2, 1) partitions=0/1, tablets=0/0, tabletList= cardinality=0, avgRowSize=8.0, numNodes=1 --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org