This is an automated email from the ASF dual-hosted git repository. yiguolei pushed a commit to branch branch-2.1 in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.1 by this push: new 55974f7fb90 [fix](mtmv) Fix materialized rewrite oom when the num of relation mapping is too large (#48887) (#49850) 55974f7fb90 is described below commit 55974f7fb90871cea17811938fafd4ceede23856 Author: seawinde <w...@selectdb.com> AuthorDate: Tue Apr 8 15:18:26 2025 +0800 [fix](mtmv) Fix materialized rewrite oom when the num of relation mapping is too large (#48887) (#49850) --- .../mv/AbstractMaterializedViewRule.java | 11 +- .../rules/exploration/mv/mapping/Mapping.java | 4 +- .../exploration/mv/mapping/RelationMapping.java | 113 +++++--- .../doris/nereids/mv/RelationMappingTest.java | 158 ++++++++++++ .../rules/exploration/mv/HyperGraphAggTest.java | 3 +- .../nereids/rules/exploration/mv/MappingTest.java | 287 ++++++++++++++++++++- .../apache/doris/nereids/sqltest/SqlTestBase.java | 3 +- .../mv/many_self_join/many_self_join.groovy | 92 +++++++ 8 files changed, 624 insertions(+), 47 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java index 33acf462fb1..e8e46b68f3d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java @@ -180,21 +180,16 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac () -> String.format("matchMode is %s", matchMode)); return rewriteResults; } + SessionVariable sessionVariable = cascadesContext.getConnectContext().getSessionVariable(); + int materializedViewRelationMappingMaxCount = sessionVariable.getMaterializedViewRelationMappingMaxCount(); List<RelationMapping> queryToViewTableMappings = RelationMapping.generate(queryStructInfo.getRelations(), - viewStructInfo.getRelations()); + viewStructInfo.getRelations(), materializedViewRelationMappingMaxCount); // if any relation in query and view can not map, bail out. if (queryToViewTableMappings == null) { materializationContext.recordFailReason(queryStructInfo, "Query to view table mapping is null", () -> ""); return rewriteResults; } - SessionVariable sessionVariable = cascadesContext.getConnectContext().getSessionVariable(); - int materializedViewRelationMappingMaxCount = sessionVariable.getMaterializedViewRelationMappingMaxCount(); - if (queryToViewTableMappings.size() > materializedViewRelationMappingMaxCount) { - LOG.warn("queryToViewTableMappings is over limit and be intercepted"); - queryToViewTableMappings = queryToViewTableMappings.subList(0, materializedViewRelationMappingMaxCount); - } - for (RelationMapping queryToViewTableMapping : queryToViewTableMappings) { SlotMapping queryToViewSlotMapping = materializationContext.getSlotMappingFromCache(queryToViewTableMapping); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/Mapping.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/Mapping.java index d9192569a0c..08b2c30275b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/Mapping.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/Mapping.java @@ -102,7 +102,9 @@ public abstract class Mapping { @Override public String toString() { - return "MappedRelation{" + "relationId=" + relationId + ", slotNameToSlotMap=" + slotNameToSlotMap + '}'; + String relationTableName = belongedRelation.getTable().getNameWithFullQualifiers(); + return "MappedRelation{" + "relationId=" + relationId + ", relationTableName=" + + relationTableName + ", slotNameToSlotMap=" + slotNameToSlotMap + '}'; } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/RelationMapping.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/RelationMapping.java index 42d4cd59b0d..3952db9b9af 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/RelationMapping.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/RelationMapping.java @@ -29,9 +29,11 @@ import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableBiMap.Builder; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Set; @@ -44,6 +46,7 @@ import java.util.Set; */ public class RelationMapping extends Mapping { + private static final Logger LOG = LogManager.getLogger(RelationMapping.class); private final ImmutableBiMap<MappedRelation, MappedRelation> mappedRelationMap; public RelationMapping(ImmutableBiMap<MappedRelation, MappedRelation> mappedRelationMap) { @@ -61,7 +64,8 @@ public class RelationMapping extends Mapping { /** * Generate mapping according to source and target relation */ - public static List<RelationMapping> generate(List<CatalogRelation> sources, List<CatalogRelation> targets) { + public static List<RelationMapping> generate(List<CatalogRelation> sources, List<CatalogRelation> targets, + int relationMappingMaxCount) { // Construct tmp map, key is the table qualifier, value is the corresponding catalog relations HashMultimap<TableIdentifier, MappedRelation> sourceTableRelationIdMap = HashMultimap.create(); for (CatalogRelation relation : sources) { @@ -92,39 +96,25 @@ public class RelationMapping extends Mapping { } // relation appear more than once, should cartesian them and power set to correct combination // if query is select * from tableA0, tableA1, materialized view is select * from tableA2, tableA3, - // tableA is the same table used by both query and materialized view - // relationMapping will be - // tableA0 tableA2 - // tableA0 tableA3 - // tableA1 tableA2 - // tableA1 tableA3 - ImmutableList<Pair<MappedRelation, MappedRelation>> relationMapping = Sets.cartesianProduct( - sourceMappedRelations, targetMappedRelations) - .stream() - .map(listPair -> Pair.of(listPair.get(0), listPair.get(1))) - .collect(ImmutableList.toImmutableList()); - - // the mapping in relationMappingPowerList should be bi-direction + // the relationMappingPowerList in relationMappingPowerList should be bi-direction // [ - // {tableA0 tableA2, tableA1 tableA3} - // {tableA0 tableA3, tableA1 tableA2} + // {tableA0 -> tableA2, tableA1 -> tableA3} + // {tableA0 -> tableA3, tableA1 -> tableA2} // ] + // query is select * from tableA0, tableA1, tableA4 List<BiMap<MappedRelation, MappedRelation>> relationMappingPowerList = new ArrayList<>(); - int relationMappingSize = relationMapping.size(); - int relationMappingMinSize = Math.min(sourceMappedRelations.size(), targetMappedRelations.size()); - for (int i = 0; i < relationMappingSize; i++) { - HashBiMap<MappedRelation, MappedRelation> relationBiMap = HashBiMap.create(); - relationBiMap.put(relationMapping.get(i).key(), relationMapping.get(i).value()); - for (int j = i + 1; j < relationMappingSize; j++) { - if (!relationBiMap.containsKey(relationMapping.get(j).key()) - && !relationBiMap.containsValue(relationMapping.get(j).value())) { - relationBiMap.put(relationMapping.get(j).key(), relationMapping.get(j).value()); - } - } - // mapping should contain min num of relation in source or target at least - if (relationBiMap.size() >= relationMappingMinSize) { - relationMappingPowerList.add(relationBiMap); + List<Pair<MappedRelation[], MappedRelation[]>> combinations = getUniquePermutation( + sourceMappedRelations.toArray(new MappedRelation[0]), + targetMappedRelations.toArray(new MappedRelation[0]), relationMappingMaxCount); + for (Pair<MappedRelation[], MappedRelation[]> combination : combinations) { + BiMap<MappedRelation, MappedRelation> combinationBiMap = HashBiMap.create(); + MappedRelation[] key = combination.key(); + MappedRelation[] value = combination.value(); + int length = Math.min(key.length, value.length); + for (int i = 0; i < length; i++) { + combinationBiMap.put(key[i], value[i]); } + relationMappingPowerList.add(combinationBiMap); } mappedRelations.add(relationMappingPowerList); } @@ -167,4 +157,65 @@ public class RelationMapping extends Mapping { public int hashCode() { return Objects.hash(mappedRelationMap); } + + /** + * Permutation and remove duplicated element + * For example: + * Given [1, 4, 5] and [191, 194, 195] + * This would return + * [ + * [(1, 191) (4, 194) (5, 195)], + * [(1, 191) (4, 195) (5, 194)], + * [(1, 194) (4, 191) (5, 195)], + * [(1, 194) (4, 195) (5, 191)], + * [(1, 195) (4, 191) (5, 194)], + * [(1, 195) (4, 194) (5, 191)] + * ] + * */ + public static List<Pair<MappedRelation[], MappedRelation[]>> getUniquePermutation( + MappedRelation[] left, MappedRelation[] right, int maxMappingCount) { + boolean needSwap = left.length > right.length; + if (needSwap) { + MappedRelation[] temp = left; + left = right; + right = temp; + } + + boolean[] used = new boolean[right.length]; + MappedRelation[] current = new MappedRelation[left.length]; + List<Pair<MappedRelation[], MappedRelation[]>> results = new ArrayList<>(); + backtrack(left, right, 0, used, current, results, maxMappingCount); + if (needSwap) { + List<Pair<MappedRelation[], MappedRelation[]>> tmpResults = results; + results = new ArrayList<>(); + for (Pair<MappedRelation[], MappedRelation[]> relation : tmpResults) { + results.add(Pair.of(relation.value(), relation.key())); + } + } + return results; + } + + private static void backtrack(MappedRelation[] left, MappedRelation[] right, int index, + boolean[] used, MappedRelation[] current, List<Pair<MappedRelation[], MappedRelation[]>> results, + int maxMappingCount) { + if (results.size() >= maxMappingCount) { + LOG.warn("queryToViewTableMappings is over limit and be intercepted, " + + "results size is {}, MappedRelation left is {}, MappedRelation right is {}", + results.size(), Arrays.toString(left), Arrays.toString(right)); + return; + } + if (index == left.length) { + results.add(Pair.of(left, Arrays.copyOf(current, current.length))); + return; + } + + for (int i = 0; i < right.length; i++) { + if (!used[i]) { + used[i] = true; + current[index] = right[i]; + backtrack(left, right, index + 1, used, current, results, maxMappingCount); + used[i] = false; + } + } + } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/RelationMappingTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/RelationMappingTest.java new file mode 100644 index 00000000000..35b110baf3b --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/RelationMappingTest.java @@ -0,0 +1,158 @@ +// 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.mv; + +import org.apache.doris.common.Pair; +import org.apache.doris.nereids.rules.exploration.mv.mapping.Mapping.MappedRelation; +import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping; +import org.apache.doris.nereids.trees.plans.RelationId; +import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import mockit.Mocked; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class RelationMappingTest { + + @Mocked + public CatalogRelation catalogRelation; + + /** + * Permutation and remove duplicated element + * For example: + * Given [1, 4, 5] and [191, 194, 195] + * This would return + * [ + * [(1, 191) (4, 194) (5, 195)], + * [(1, 191) (4, 195) (5, 194)], + * [(1, 194) (4, 191) (5, 195)], + * [(1, 194) (4, 195) (5, 191)], + * [(1, 195) (4, 191) (5, 194)], + * [(1, 195) (4, 194) (5, 191)] + * ] + * */ + @Test + public void testGetPermutation() { + + MappedRelation mr1 = MappedRelation.of(new RelationId(1), catalogRelation); + MappedRelation mr4 = MappedRelation.of(new RelationId(4), catalogRelation); + MappedRelation mr5 = MappedRelation.of(new RelationId(5), catalogRelation); + + MappedRelation mr191 = MappedRelation.of(new RelationId(191), catalogRelation); + MappedRelation mr194 = MappedRelation.of(new RelationId(194), catalogRelation); + MappedRelation mr195 = MappedRelation.of(new RelationId(195), catalogRelation); + + MappedRelation[] left = new MappedRelation[] {mr1, mr4, mr5}; + MappedRelation[] right = new MappedRelation[] {mr191, mr194, mr195}; + + List<Pair<MappedRelation[], MappedRelation[]>> uniquePermutation = RelationMapping.getUniquePermutation(left, + right, 10); + Set<BiMap<Integer, Integer>> generatedMappedSet = generateDirectPermutations(uniquePermutation); + + Set<BiMap<Integer, Integer>> exceptedSet = new HashSet<>(); + BiMap<Integer, Integer> eachMap = HashBiMap.create(); + eachMap.put(1, 195); + eachMap.put(4, 194); + eachMap.put(5, 191); + exceptedSet.add(eachMap); + eachMap = HashBiMap.create(); + eachMap.put(1, 194); + eachMap.put(4, 191); + eachMap.put(5, 195); + exceptedSet.add(eachMap); + eachMap = HashBiMap.create(); + eachMap.put(1, 194); + eachMap.put(4, 195); + eachMap.put(5, 191); + exceptedSet.add(eachMap); + eachMap = HashBiMap.create(); + eachMap.put(1, 195); + eachMap.put(4, 191); + eachMap.put(5, 194); + exceptedSet.add(eachMap); + eachMap = HashBiMap.create(); + eachMap.put(1, 191); + eachMap.put(4, 194); + eachMap.put(5, 195); + exceptedSet.add(eachMap); + eachMap = HashBiMap.create(); + eachMap.put(1, 191); + eachMap.put(4, 195); + eachMap.put(5, 194); + exceptedSet.add(eachMap); + + Assertions.assertEquals(exceptedSet, generatedMappedSet); + } + + /** + * Permutation and remove duplicated element + * For example: + * Given [1, 4, 5] and [191, 194, 195] + * This would return + * [ + * [(1, 191) (4, 194) (5, 195)], + * [(1, 191) (4, 195) (5, 194)], + * [(1, 194) (4, 191) (5, 195)], + * [(1, 194) (4, 195) (5, 191)], + * [(1, 195) (4, 191) (5, 194)], + * [(1, 195) (4, 194) (5, 191)] + * ] + * */ + @Test + public void testGetPermutationWithLimit() { + + MappedRelation mr1 = MappedRelation.of(new RelationId(1), catalogRelation); + MappedRelation mr4 = MappedRelation.of(new RelationId(4), catalogRelation); + MappedRelation mr5 = MappedRelation.of(new RelationId(5), catalogRelation); + + MappedRelation mr191 = MappedRelation.of(new RelationId(191), catalogRelation); + MappedRelation mr194 = MappedRelation.of(new RelationId(194), catalogRelation); + MappedRelation mr195 = MappedRelation.of(new RelationId(195), catalogRelation); + + MappedRelation[] left = new MappedRelation[] {mr1, mr4, mr5}; + MappedRelation[] right = new MappedRelation[] {mr191, mr194, mr195}; + + List<Pair<MappedRelation[], MappedRelation[]>> uniquePermutation = RelationMapping.getUniquePermutation(left, + right, 5); + + Assertions.assertEquals(5, uniquePermutation.size()); + } + + private Set<BiMap<Integer, Integer>> generateDirectPermutations( + List<Pair<MappedRelation[], MappedRelation[]>> uniquePermutation) { + Set<BiMap<Integer, Integer>> relationMappingPowerSet = new HashSet<>(); + for (Pair<MappedRelation[], MappedRelation[]> combination : uniquePermutation) { + BiMap<Integer, Integer> combinationBiMap = HashBiMap.create(); + MappedRelation[] key = combination.key(); + MappedRelation[] value = combination.value(); + int length = Math.min(key.length, value.length); + for (int i = 0; i < length; i++) { + combinationBiMap.put(key[i].getRelationId().asInt(), value[i].getRelationId().asInt()); + } + relationMappingPowerSet.add(combinationBiMap); + } + return relationMappingPowerSet; + } + +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphAggTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphAggTest.java index 44939cc61c6..b62c41db00d 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphAggTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphAggTest.java @@ -93,7 +93,8 @@ class HyperGraphAggTest extends SqlTestBase { null, new BitSet()).get(0); StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2, p2, null, new BitSet()).get(0); - RelationMapping rm = RelationMapping.generate(st1.getRelations(), st2.getRelations()).get(0); + RelationMapping rm = RelationMapping.generate(st1.getRelations(), st2.getRelations(), 8) + .get(0); SlotMapping sm = SlotMapping.generate(rm); return LogicalCompatibilityContext.from(rm, sm, st1, st2); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java index 4371b9f5785..509acfba207 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java @@ -33,7 +33,9 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * MappingTest @@ -105,7 +107,8 @@ public class MappingTest extends TestWithFeService { List<CatalogRelation> targetRelations = new ArrayList<>(); targetPlan.accept(RelationCollector.INSTANCE, targetRelations); - List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations); + List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations, + 8); Assertions.assertNotNull(generateRelationMapping); Assertions.assertEquals(1, generateRelationMapping.size()); @@ -154,7 +157,8 @@ public class MappingTest extends TestWithFeService { List<CatalogRelation> targetRelations = new ArrayList<>(); targetPlan.accept(RelationCollector.INSTANCE, targetRelations); - List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations); + List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations, + 8); Assertions.assertNotNull(generateRelationMapping); Assertions.assertEquals(1, generateRelationMapping.size()); @@ -200,7 +204,8 @@ public class MappingTest extends TestWithFeService { List<CatalogRelation> targetRelations = new ArrayList<>(); targetPlan.accept(RelationCollector.INSTANCE, targetRelations); - List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations); + List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations, + 8); Assertions.assertNotNull(generateRelationMapping); Assertions.assertEquals(1, generateRelationMapping.size()); @@ -246,7 +251,8 @@ public class MappingTest extends TestWithFeService { List<CatalogRelation> targetRelations = new ArrayList<>(); targetPlan.accept(RelationCollector.INSTANCE, targetRelations); - List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations); + List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations, + 8); Assertions.assertNotNull(generateRelationMapping); Assertions.assertEquals(2, generateRelationMapping.size()); @@ -306,7 +312,8 @@ public class MappingTest extends TestWithFeService { List<CatalogRelation> targetRelations = new ArrayList<>(); targetPlan.accept(RelationCollector.INSTANCE, targetRelations); - List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations); + List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations, + 8); Assertions.assertNotNull(generateRelationMapping); Assertions.assertEquals(2, generateRelationMapping.size()); @@ -343,6 +350,262 @@ public class MappingTest extends TestWithFeService { assertRelationMapping(generateRelationMapping.get(0), expectedRelationMapping, expectedSlotMapping); } + // Test more than two tables, and the same table num in source plan is equals to target plan + @Test + public void testGenerateMapping6() { + Plan sourcePlan = PlanChecker.from(connectContext) + .analyze("SELECT orders.*, l1.l_orderkey, l2.l_orderkey, l3.l_orderkey " + + "FROM\n" + + " orders,\n" + + " lineitem l1,\n" + + " lineitem l2,\n" + + " lineitem l3\n" + + "WHERE\n" + + " l1.l_orderkey = l2.l_orderkey and l1.l_orderkey = l3.l_orderkey\n" + + " AND l1.l_orderkey = o_orderkey") + .getPlan(); + + Plan targetPlan = PlanChecker.from(connectContext) + .analyze("SELECT orders.*, l1.l_orderkey, l2.l_orderkey, l3.l_orderkey " + + "FROM\n" + + " lineitem l1,\n" + + " orders,\n" + + " lineitem l2,\n" + + " lineitem l3\n" + + "WHERE\n" + + " l1.l_orderkey = l2.l_orderkey and l1.l_orderkey = l3.l_orderkey\n" + + " AND l2.l_orderkey = o_orderkey") + .getPlan(); + List<CatalogRelation> sourceRelations = new ArrayList<>(); + sourcePlan.accept(RelationCollector.INSTANCE, sourceRelations); + + List<CatalogRelation> targetRelations = new ArrayList<>(); + targetPlan.accept(RelationCollector.INSTANCE, targetRelations); + + List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations, + 8); + Assertions.assertNotNull(generateRelationMapping); + Assertions.assertEquals(6, generateRelationMapping.size()); + + // expected table relation mapping is as following + // (1, 3), (2, 2), (3, 0), (0, 1) + // (1, 0), (2, 3), (3, 2), (0, 1) + // (1, 2), (2, 3), (3, 0), (0, 1) + // (1, 0), (2, 2), (3, 3), (0, 1) + // (1, 2), (2, 0), (3, 3), (0, 1) + // (1, 3), (2, 0), (3, 2), (0, 1) + Set<BiMap<RelationId, RelationId>> expectedRelationMappingSet = new HashSet<>(); + BiMap<RelationId, RelationId> expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(3)); + expectedRelationMapping.put(new RelationId(2), new RelationId(2)); + expectedRelationMapping.put(new RelationId(3), new RelationId(0)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(0)); + expectedRelationMapping.put(new RelationId(2), new RelationId(3)); + expectedRelationMapping.put(new RelationId(3), new RelationId(2)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(2)); + expectedRelationMapping.put(new RelationId(2), new RelationId(3)); + expectedRelationMapping.put(new RelationId(3), new RelationId(0)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(0)); + expectedRelationMapping.put(new RelationId(2), new RelationId(2)); + expectedRelationMapping.put(new RelationId(3), new RelationId(3)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(2)); + expectedRelationMapping.put(new RelationId(2), new RelationId(0)); + expectedRelationMapping.put(new RelationId(3), new RelationId(3)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(3)); + expectedRelationMapping.put(new RelationId(2), new RelationId(0)); + expectedRelationMapping.put(new RelationId(3), new RelationId(2)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + assertRelationMapping(new HashSet<>(generateRelationMapping), expectedRelationMappingSet); + } + + // Test more than two tables, and the same table num in source plan is less then the num of target plan + @Test + public void testGenerateMapping7() { + Plan sourcePlan = PlanChecker.from(connectContext) + .analyze("SELECT orders.*, l1.l_orderkey, l3.l_orderkey " + + "FROM\n" + + " orders,\n" + + " lineitem l1,\n" + + " lineitem l3\n" + + "WHERE\n" + + " l1.l_orderkey = l3.l_orderkey\n" + + " AND l1.l_orderkey = o_orderkey") + .getPlan(); + + Plan targetPlan = PlanChecker.from(connectContext) + .analyze("SELECT orders.*, l1.l_orderkey, l2.l_orderkey, l3.l_orderkey " + + "FROM\n" + + " lineitem l1,\n" + + " orders,\n" + + " lineitem l2,\n" + + " lineitem l3\n" + + "WHERE\n" + + " l1.l_orderkey = l2.l_orderkey and l1.l_orderkey = l3.l_orderkey\n" + + " AND l2.l_orderkey = o_orderkey") + .getPlan(); + List<CatalogRelation> sourceRelations = new ArrayList<>(); + sourcePlan.accept(RelationCollector.INSTANCE, sourceRelations); + + List<CatalogRelation> targetRelations = new ArrayList<>(); + targetPlan.accept(RelationCollector.INSTANCE, targetRelations); + + List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations, + 8); + Assertions.assertNotNull(generateRelationMapping); + Assertions.assertEquals(6, generateRelationMapping.size()); + + // expected table relation mapping is as following + // (1, 2), (2, 0), (0, 1) + // (1, 2), (2, 3), (0, 1) + // (1, 0), (2, 2), (0, 1) + // (1, 0), (2, 3), (0, 1) + // (1, 3), (2, 0), (0, 1) + // (1, 3), (2, 2), (0, 1) + Set<BiMap<RelationId, RelationId>> expectedRelationMappingSet = new HashSet<>(); + BiMap<RelationId, RelationId> expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(2)); + expectedRelationMapping.put(new RelationId(2), new RelationId(0)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(2)); + expectedRelationMapping.put(new RelationId(2), new RelationId(3)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(0)); + expectedRelationMapping.put(new RelationId(2), new RelationId(2)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(0)); + expectedRelationMapping.put(new RelationId(2), new RelationId(3)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(3)); + expectedRelationMapping.put(new RelationId(2), new RelationId(0)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(3)); + expectedRelationMapping.put(new RelationId(2), new RelationId(2)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + assertRelationMapping(new HashSet<>(generateRelationMapping), expectedRelationMappingSet); + } + + // Test more than two tables, and the same table num in source plan is more then the num of target plan + @Test + public void testGenerateMapping8() { + Plan sourcePlan = PlanChecker.from(connectContext) + .analyze("SELECT orders.*, l1.l_orderkey, l2.l_orderkey, l3.l_orderkey " + + "FROM\n" + + " orders,\n" + + " lineitem l1,\n" + + " lineitem l2,\n" + + " lineitem l3\n" + + "WHERE\n" + + " l1.l_orderkey = l2.l_orderkey and l1.l_orderkey = l3.l_orderkey\n" + + " AND l1.l_orderkey = o_orderkey") + .getPlan(); + + Plan targetPlan = PlanChecker.from(connectContext) + .analyze("SELECT orders.*, l1.l_orderkey, l3.l_orderkey " + + "FROM\n" + + " lineitem l1,\n" + + " orders,\n" + + " lineitem l3\n" + + "WHERE\n" + + " l1.l_orderkey = l3.l_orderkey\n" + + " AND l3.l_orderkey = o_orderkey") + .getPlan(); + List<CatalogRelation> sourceRelations = new ArrayList<>(); + sourcePlan.accept(RelationCollector.INSTANCE, sourceRelations); + + List<CatalogRelation> targetRelations = new ArrayList<>(); + targetPlan.accept(RelationCollector.INSTANCE, targetRelations); + + List<RelationMapping> generateRelationMapping = RelationMapping.generate(sourceRelations, targetRelations, + 8); + Assertions.assertNotNull(generateRelationMapping); + Assertions.assertEquals(6, generateRelationMapping.size()); + + // expected table relation mapping is as following + // (1, 0), (2, 2), (0, 1) + // (1, 0), (3, 2), (0, 1) + // (2, 0), (1, 2), (0, 1) + // (3, 0), (2, 2), (0, 1) + // (2, 0), (3, 2), (0, 1) + // (3, 0), (1, 2), (0, 1) + Set<BiMap<RelationId, RelationId>> expectedRelationMappingSet = new HashSet<>(); + BiMap<RelationId, RelationId> expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(0)); + expectedRelationMapping.put(new RelationId(2), new RelationId(2)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(1), new RelationId(0)); + expectedRelationMapping.put(new RelationId(3), new RelationId(2)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(2), new RelationId(0)); + expectedRelationMapping.put(new RelationId(1), new RelationId(2)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(3), new RelationId(0)); + expectedRelationMapping.put(new RelationId(2), new RelationId(2)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(2), new RelationId(0)); + expectedRelationMapping.put(new RelationId(3), new RelationId(2)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + expectedRelationMapping = HashBiMap.create(); + expectedRelationMapping.put(new RelationId(3), new RelationId(0)); + expectedRelationMapping.put(new RelationId(1), new RelationId(2)); + expectedRelationMapping.put(new RelationId(0), new RelationId(1)); + expectedRelationMappingSet.add(expectedRelationMapping); + + assertRelationMapping(new HashSet<>(generateRelationMapping), expectedRelationMappingSet); + } + private void assertRelationMapping(RelationMapping relationMapping, BiMap<RelationId, RelationId> expectRelationMapping, BiMap<ExprId, ExprId> expectSlotMapping) { @@ -362,6 +625,20 @@ public class MappingTest extends TestWithFeService { Assertions.assertEquals(generatedSlotMapping, expectSlotMapping); } + private void assertRelationMapping(Set<RelationMapping> relationMapping, + Set<BiMap<RelationId, RelationId>> expectRelationMapping) { + // check relation mapping if equals or not + Set<BiMap<RelationId, RelationId>> relationMappingSet = new HashSet<>(); + relationMapping.forEach(mapping -> { + BiMap<RelationId, RelationId> generatedRelationMapping = HashBiMap.create(); + mapping.getMappedRelationMap().forEach((key, value) -> + generatedRelationMapping.put(key.getRelationId(), value.getRelationId())); + relationMappingSet.add(generatedRelationMapping); + } + ); + Assertions.assertEquals(relationMappingSet, expectRelationMapping); + } + protected static class RelationCollector extends DefaultPlanVisitor<Void, List<CatalogRelation>> { public static final RelationCollector INSTANCE = new RelationCollector(); diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlTestBase.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlTestBase.java index 9ebbc22a85c..46e121d167c 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlTestBase.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlTestBase.java @@ -108,7 +108,8 @@ public abstract class SqlTestBase extends TestWithFeService implements MemoPatte context, new BitSet()).get(0); StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2, p2, context, new BitSet()).get(0); - RelationMapping rm = RelationMapping.generate(st1.getRelations(), st2.getRelations()).get(0); + RelationMapping rm = RelationMapping.generate(st1.getRelations(), st2.getRelations(), 8) + .get(0); SlotMapping sm = SlotMapping.generate(rm); return LogicalCompatibilityContext.from(rm, sm, st1, st2); } diff --git a/regression-test/suites/nereids_rules_p0/mv/many_self_join/many_self_join.groovy b/regression-test/suites/nereids_rules_p0/mv/many_self_join/many_self_join.groovy new file mode 100644 index 00000000000..67e558dc5d8 --- /dev/null +++ b/regression-test/suites/nereids_rules_p0/mv/many_self_join/many_self_join.groovy @@ -0,0 +1,92 @@ +// 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("many_self_join") { + + def db_name = context.config.getDbNameByFile(context.file) + def tb_name = db_name + "_tb" + def mv_name = db_name + "_mtmv" + sql """drop table if exists ${tb_name}""" + sql """ + CREATE TABLE ${tb_name} ( + employee_id INT, + employee_name VARCHAR(100), + manager_id INT + ) ENGINE=OLAP + DUPLICATE KEY(employee_id) + COMMENT 'OLAP' + DISTRIBUTED BY HASH(`employee_id`) BUCKETS 1 + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1" + ); + """ + sql """ + INSERT INTO ${tb_name} (employee_id, employee_name, manager_id) VALUES + (1, 'CEO', 1), + (2, 'Manager1', 1), + (3, 'Manager2', 1) + """ + + sql """drop MATERIALIZED VIEW if exists ${mv_name};""" + def mtmv_sql = """SELECT + e1.employee_name AS employee, + e2.employee_name AS manager_1, + e3.employee_name AS manager_2, + e4.employee_name AS manager_3, + e5.employee_name AS manager_4, + e6.employee_name AS manager_5, + e7.employee_name AS manager_6, + e8.employee_name AS manager_7, + e9.employee_name AS manager_8, + e10.employee_name AS manager_9 + FROM + ${tb_name} e1 + LEFT JOIN + ${tb_name} e2 ON e1.manager_id = e2.employee_id + LEFT JOIN + ${tb_name} e3 ON e2.manager_id = e3.employee_id + LEFT JOIN + ${tb_name} e4 ON e3.manager_id = e4.employee_id + LEFT JOIN + ${tb_name} e5 ON e4.manager_id = e5.employee_id + LEFT JOIN + ${tb_name} e6 ON e5.manager_id = e6.employee_id + LEFT JOIN + ${tb_name} e7 ON e6.manager_id = e7.employee_id + LEFT JOIN + ${tb_name} e8 ON e7.manager_id = e8.employee_id + LEFT JOIN + ${tb_name} e9 ON e8.manager_id = e9.employee_id + LEFT JOIN + ${tb_name} e10 ON e9.manager_id = e10.employee_id""" + + create_async_mv(db_name, mv_name, mtmv_sql) + + def mark = true + sql """USE ${db_name}""" + sql """set enable_nereids_timeout = true;""" + try { + mv_rewrite_success(mtmv_sql, mv_name) + } catch (Exception e) { + log.info("e.getMessage(): " + e.getMessage()) + mark = false + } + + assertTrue(mark) + +} + --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org