This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.2 by this push:
new 6db5d4285ff branch-4.2: [fix](constraint) Preserve table identity in
foreign key join elimination #67699 (#70045)
6db5d4285ff is described below
commit 6db5d4285ffe1a285b5f14757cfb5116aa25aac7
Author: morrySnow <[email protected]>
AuthorDate: Tue Nov 3 17:25:37 2026 +0800
branch-4.2: [fix](constraint) Preserve table identity in foreign key join
elimination #67699 (#70045)
picked from master #67699
---
.../nereids/rules/rewrite/ForeignKeyContext.java | 63 +++++++++++++++++-----
.../rules/rewrite/EliminateJoinByFkTest.java | 18 +++++++
2 files changed, 68 insertions(+), 13 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ForeignKeyContext.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ForeignKeyContext.java
index 52021622ff8..731599a0ad3 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ForeignKeyContext.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ForeignKeyContext.java
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.rules.rewrite;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.TableIf;
+import org.apache.doris.catalog.constraint.TableIdentifier;
import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
@@ -36,6 +37,7 @@ import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@@ -43,10 +45,10 @@ import java.util.stream.Collectors;
* Record Foreign Key Context
*/
public class ForeignKeyContext {
- Set<Map<Column, Column>> constraints = new HashSet<>();
- Set<Column> foreignKeys = new HashSet<>();
- Set<Column> primaryKeys = new HashSet<>();
- Map<Slot, Column> slotToColumn = new HashMap<>();
+ Set<Map<QualifiedColumn, QualifiedColumn>> constraints = new HashSet<>();
+ Set<QualifiedColumn> foreignKeys = new HashSet<>();
+ Set<QualifiedColumn> primaryKeys = new HashSet<>();
+ Map<Slot, QualifiedColumn> slotToColumn = new HashMap<>();
Map<Slot, Set<Expression>> slotWithPredicates = new HashMap<>();
/**
@@ -66,12 +68,13 @@ public class ForeignKeyContext {
@Override
public Void visitLogicalRelation(LogicalRelation relation,
ForeignKeyContext context) {
if (relation instanceof LogicalCatalogRelation) {
- context.putAllForeignKeys(((LogicalCatalogRelation)
relation).getTable());
- context.putAllPrimaryKeys(((LogicalCatalogRelation)
relation).getTable());
+ TableIf table = ((LogicalCatalogRelation)
relation).getTable();
+ context.putAllForeignKeys(table);
+ context.putAllPrimaryKeys(table);
relation.getOutput().stream()
.filter(SlotReference.class::isInstance)
.map(SlotReference.class::cast)
- .forEach(context::putSlot);
+ .forEach(slot -> context.putSlot(slot, table));
}
return null;
}
@@ -99,15 +102,21 @@ public class ForeignKeyContext {
void putAllForeignKeys(TableIf table) {
table.getForeignKeyConstraints().forEach(c -> {
- Map<Column, Column> constraint = c.getForeignToPrimary(table);
- constraints.add(c.getForeignToPrimary(table));
+ TableIf referencedTable = c.getReferencedTable();
+ Map<QualifiedColumn, QualifiedColumn> constraint =
c.getForeignToReference().entrySet().stream()
+ .collect(ImmutableMap.toImmutableMap(
+ entry -> new QualifiedColumn(table,
table.getColumn(entry.getKey())),
+ entry -> new QualifiedColumn(
+ referencedTable,
referencedTable.getColumn(entry.getValue()))));
+ constraints.add(constraint);
foreignKeys.addAll(constraint.keySet());
});
}
void putAllPrimaryKeys(TableIf table) {
table.getPrimaryKeyConstraints().forEach(c -> {
- Set<Column> primaryKey = c.getPrimaryKeys(table);
+ Set<QualifiedColumn> primaryKey = c.getPrimaryKeys(table).stream()
+ .map(column -> new QualifiedColumn(table,
column)).collect(Collectors.toSet());
primaryKeys.addAll(primaryKey);
});
}
@@ -122,12 +131,12 @@ public class ForeignKeyContext {
key.stream().map(s ->
slotToColumn.get(s)).collect(Collectors.toSet()));
}
- void putSlot(SlotReference slot) {
+ void putSlot(SlotReference slot, TableIf table) {
if (!slot.getOriginalColumn().isPresent()) {
return;
}
Column c = slot.getOriginalColumn().get();
- slotToColumn.put(slot, c);
+ slotToColumn.put(slot, new QualifiedColumn(table, c));
}
void putAlias(Slot newSlot, Slot originSlot) {
@@ -171,7 +180,7 @@ public class ForeignKeyContext {
* Check whether the given mapping relation satisfies any constraints
*/
public boolean satisfyConstraint(Map<Slot, Slot> primaryToForeign) {
- Map<Column, Column> foreignToPrimary =
primaryToForeign.entrySet().stream()
+ Map<QualifiedColumn, QualifiedColumn> foreignToPrimary =
primaryToForeign.entrySet().stream()
.collect(ImmutableMap.toImmutableMap(
e -> slotToColumn.get(e.getValue()),
e -> slotToColumn.get(e.getKey())));
@@ -203,4 +212,32 @@ public class ForeignKeyContext {
return
slotWithPredicates.get(pf.getValue()).containsAll(primaryPredicates);
});
}
+
+ /** A column identity qualified by its owning table. */
+ private static final class QualifiedColumn {
+ private final TableIdentifier tableIdentifier;
+ private final Column column;
+
+ private QualifiedColumn(TableIf table, Column column) {
+ this.tableIdentifier = new TableIdentifier(table);
+ this.column = column;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof QualifiedColumn)) {
+ return false;
+ }
+ QualifiedColumn other = (QualifiedColumn) obj;
+ return tableIdentifier.equals(other.tableIdentifier) &&
column.equals(other.column);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(tableIdentifier, column);
+ }
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/EliminateJoinByFkTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/EliminateJoinByFkTest.java
index 0d54ad4f1e5..bbd2046f53a 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/EliminateJoinByFkTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/EliminateJoinByFkTest.java
@@ -68,9 +68,16 @@ class EliminateJoinByFkTest extends TestWithFeService
implements MemoPatternMatc
+ ")\n"
+ "UNIQUE KEY(id3)\n"
+ "DISTRIBUTED BY HASH(id3) BUCKETS 10\n"
+ + "PROPERTIES (\"replication_num\" = \"1\")\n",
+ "CREATE TABLE IF NOT EXISTS unrelated_pri (\n"
+ + " id1 int not null\n"
+ + ")\n"
+ + "UNIQUE KEY(id1)\n"
+ + "DISTRIBUTED BY HASH(id1) BUCKETS 10\n"
+ "PROPERTIES (\"replication_num\" = \"1\")\n"
);
addConstraint("Alter table pri add constraint pk primary key (id1)");
+ addConstraint("Alter table unrelated_pri add constraint unrelated_pk
primary key (id1)");
addConstraint("Alter table foreign_not_null add constraint f_not_null
foreign key (id2)\n"
+ "references pri(id1)");
addConstraint("Alter table foreign_null add constraint f_not_null
foreign key (id3)\n"
@@ -106,6 +113,17 @@ class EliminateJoinByFkTest extends TestWithFeService
implements MemoPatternMatc
.printlnTree();
}
+ @Test
+ void testDoNotEliminateJoinWithUnrelatedPrimaryTable() {
+ String sql = "select foreign_not_null.id2 from unrelated_pri "
+ + "inner join foreign_not_null on unrelated_pri.id1 =
foreign_not_null.id2";
+ PlanChecker.from(connectContext)
+ .analyze(sql)
+ .rewrite()
+ .matches(logicalJoin())
+ .printlnTree();
+ }
+
@Test
void testNotNullWithPredicate() {
String sql = "select pri.id1 from pri inner join foreign_not_null on
pri.id1 = foreign_not_null.id2\n"
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]