This is an automated email from the ASF dual-hosted git repository.
starocean999 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 5b3b20c2f8b [fix](asof_join)PhysicalHashJoin's computeUniform method
should process asof join properly (#62730)
5b3b20c2f8b is described below
commit 5b3b20c2f8bcea8cc03d0a3472336e112a90dd31
Author: starocean999 <[email protected]>
AuthorDate: Tue Jun 2 17:48:27 2026 +0800
[fix](asof_join)PhysicalHashJoin's computeUniform method should process
asof join properly (#62730)
### What problem does this PR solve?
Extend PhysicalHashJoin to correctly handle ASOF join variants so trait
propagation, equal-set extraction, and functional dependency (FD)
calculations work for ASOF join
---
.../trees/plans/physical/PhysicalHashJoin.java | 4 +
.../properties/ChildOutputPropertyDeriverTest.java | 167 ++++++++++++++++++++-
2 files changed, 170 insertions(+), 1 deletion(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHashJoin.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHashJoin.java
index 7b18b01865e..a8376464f11 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHashJoin.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHashJoin.java
@@ -305,6 +305,8 @@ public class PhysicalHashJoin<
}
switch (joinType) {
case INNER_JOIN:
+ case ASOF_LEFT_INNER_JOIN:
+ case ASOF_RIGHT_INNER_JOIN:
case CROSS_JOIN:
builder.addUniformSlot(left().getLogicalProperties().getTrait());
builder.addUniformSlot(right().getLogicalProperties().getTrait());
@@ -319,10 +321,12 @@ public class PhysicalHashJoin<
builder.addUniformSlot(right().getLogicalProperties().getTrait());
break;
case LEFT_OUTER_JOIN:
+ case ASOF_LEFT_OUTER_JOIN:
builder.addUniformSlot(left().getLogicalProperties().getTrait());
builder.addUniformSlotForOuterJoinNullableSide(right().getLogicalProperties().getTrait());
break;
case RIGHT_OUTER_JOIN:
+ case ASOF_RIGHT_OUTER_JOIN:
builder.addUniformSlot(right().getLogicalProperties().getTrait());
builder.addUniformSlotForOuterJoinNullableSide(left().getLogicalProperties().getTrait());
break;
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriverTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriverTest.java
index c2fa770890f..fd23de7a2f4 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriverTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriverTest.java
@@ -45,6 +45,7 @@ import org.apache.doris.nereids.trees.plans.LimitPhase;
import org.apache.doris.nereids.trees.plans.RelationId;
import org.apache.doris.nereids.trees.plans.SortPhase;
import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
+import org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalPlan;
import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
import org.apache.doris.nereids.trees.plans.physical.PhysicalHashAggregate;
import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
@@ -590,7 +591,7 @@ class ChildOutputPropertyDeriverTest {
PhysicalHashJoin<GroupPlan, GroupPlan> join = new
PhysicalHashJoin<>(JoinType.INNER_JOIN,
Lists.newArrayList(new EqualTo(
leftSlot, rightSlot
- )),
+ )),
ExpressionUtils.EMPTY_CONDITION, new
DistributeHint(DistributeType.NONE),
Optional.empty(), logicalProperties, leftGroupPlan,
rightGroupPlan);
GroupExpression groupExpression = new GroupExpression(join);
@@ -971,4 +972,168 @@ class ChildOutputPropertyDeriverTest {
PhysicalProperties phyProp3 =
ChildOutputPropertyDeriver.computeProjectOutputProperties(projects3, hashC1);
Assertions.assertEquals(hashC1, phyProp3);
}
+
+ @Test
+ void testComputeUniformAfterRecomputeLogicalProperties() {
+ // left child has a uniform slot, right child empty
+ SlotReference leftSlot = new SlotReference(new ExprId(100), "left",
IntegerType.INSTANCE, false,
+ Collections.emptyList());
+ SlotReference rightSlot = new SlotReference(new ExprId(101), "right",
IntegerType.INSTANCE, false,
+ Collections.emptyList());
+ List<Slot> leftOutput = Lists.newArrayList(leftSlot);
+ List<Slot> rightOutput = Lists.newArrayList(rightSlot);
+
+ DataTrait.Builder leftBuilder = new DataTrait.Builder();
+ leftBuilder.addUniformSlot(leftSlot);
+ DataTrait leftTrait = leftBuilder.build();
+
+ LogicalProperties leftLogical = new LogicalProperties(() ->
leftOutput, () -> leftTrait);
+ LogicalProperties rightLogical = new LogicalProperties(() ->
rightOutput, () -> DataTrait.EMPTY_TRAIT);
+
+ IdGenerator<GroupId> idGenerator = GroupId.createGenerator();
+ GroupPlan leftGroupPlan = new GroupPlan(new
Group(idGenerator.getNextId(), leftLogical));
+ GroupPlan rightGroupPlan = new GroupPlan(new
Group(idGenerator.getNextId(), rightLogical));
+
+ PhysicalHashJoin<GroupPlan, GroupPlan> join = new
PhysicalHashJoin<>(JoinType.ASOF_LEFT_OUTER_JOIN,
+ ExpressionUtils.EMPTY_CONDITION,
ExpressionUtils.EMPTY_CONDITION,
+ new DistributeHint(DistributeType.NONE), Optional.empty(),
logicalProperties,
+ leftGroupPlan, rightGroupPlan);
+
+ // simulate physical-tree logical prop recompute by resetting logical
properties on the join
+ AbstractPhysicalPlan processed = (AbstractPhysicalPlan)
join.resetLogicalProperties();
+ processed = (AbstractPhysicalPlan)
processed.copyStatsAndGroupIdFrom((AbstractPhysicalPlan) join);
+
+ Assertions.assertInstanceOf(PhysicalHashJoin.class, processed);
+
+ DataTrait.Builder builder = new DataTrait.Builder();
+ processed.computeUniform(builder);
+ DataTrait result = builder.build();
+
+ // left slot should still be recognized as uniform after recompute
+ Assertions.assertTrue(result.isUniformAndNotNull(leftSlot));
+ }
+
+ @Test
+ void testComputeUniformAfterRecomputeLogicalProperties_AsOfLeftInner() {
+ SlotReference leftSlot = new SlotReference(new ExprId(200), "l",
IntegerType.INSTANCE, false,
+ Collections.emptyList());
+ SlotReference rightSlot = new SlotReference(new ExprId(201), "r",
IntegerType.INSTANCE, false,
+ Collections.emptyList());
+ List<Slot> leftOutput = Lists.newArrayList(leftSlot);
+ List<Slot> rightOutput = Lists.newArrayList(rightSlot);
+
+ DataTrait.Builder leftBuilder = new DataTrait.Builder();
+ leftBuilder.addUniformSlot(leftSlot);
+ DataTrait leftTrait = leftBuilder.build();
+
+ DataTrait.Builder rightBuilder = new DataTrait.Builder();
+ rightBuilder.addUniformSlot(rightSlot);
+ DataTrait rightTrait = rightBuilder.build();
+
+ LogicalProperties leftLogical = new LogicalProperties(() ->
leftOutput, () -> leftTrait);
+ LogicalProperties rightLogical = new LogicalProperties(() ->
rightOutput, () -> rightTrait);
+
+ IdGenerator<GroupId> idGenerator = GroupId.createGenerator();
+ GroupPlan leftGroupPlan = new GroupPlan(new
Group(idGenerator.getNextId(), leftLogical));
+ GroupPlan rightGroupPlan = new GroupPlan(new
Group(idGenerator.getNextId(), rightLogical));
+
+ PhysicalHashJoin<GroupPlan, GroupPlan> join = new
PhysicalHashJoin<>(JoinType.ASOF_LEFT_INNER_JOIN,
+ ExpressionUtils.EMPTY_CONDITION,
ExpressionUtils.EMPTY_CONDITION,
+ new DistributeHint(DistributeType.NONE), Optional.empty(),
logicalProperties,
+ leftGroupPlan, rightGroupPlan);
+
+ // simulate physical-tree logical prop recompute by resetting logical
properties on the join
+ AbstractPhysicalPlan processed = (AbstractPhysicalPlan)
join.resetLogicalProperties();
+ processed = (AbstractPhysicalPlan)
processed.copyStatsAndGroupIdFrom((AbstractPhysicalPlan) join);
+
+ Assertions.assertInstanceOf(PhysicalHashJoin.class, processed);
+
+ DataTrait.Builder builder = new DataTrait.Builder();
+ processed.computeUniform(builder);
+ DataTrait result = builder.build();
+
+ Assertions.assertTrue(result.isUniformAndNotNull(leftSlot));
+ Assertions.assertTrue(result.isUniformAndNotNull(rightSlot));
+ }
+
+ @Test
+ void testComputeUniformAfterRecomputeLogicalProperties_AsOfRightInner() {
+ SlotReference leftSlot = new SlotReference(new ExprId(300), "l2",
IntegerType.INSTANCE, false,
+ Collections.emptyList());
+ SlotReference rightSlot = new SlotReference(new ExprId(301), "r2",
IntegerType.INSTANCE, false,
+ Collections.emptyList());
+ List<Slot> leftOutput = Lists.newArrayList(leftSlot);
+ List<Slot> rightOutput = Lists.newArrayList(rightSlot);
+
+ DataTrait.Builder leftBuilder = new DataTrait.Builder();
+ leftBuilder.addUniformSlot(leftSlot);
+ DataTrait leftTrait = leftBuilder.build();
+
+ DataTrait.Builder rightBuilder = new DataTrait.Builder();
+ rightBuilder.addUniformSlot(rightSlot);
+ DataTrait rightTrait = rightBuilder.build();
+
+ LogicalProperties leftLogical = new LogicalProperties(() ->
leftOutput, () -> leftTrait);
+ LogicalProperties rightLogical = new LogicalProperties(() ->
rightOutput, () -> rightTrait);
+
+ IdGenerator<GroupId> idGenerator = GroupId.createGenerator();
+ GroupPlan leftGroupPlan = new GroupPlan(new
Group(idGenerator.getNextId(), leftLogical));
+ GroupPlan rightGroupPlan = new GroupPlan(new
Group(idGenerator.getNextId(), rightLogical));
+
+ PhysicalHashJoin<GroupPlan, GroupPlan> join = new
PhysicalHashJoin<>(JoinType.ASOF_RIGHT_INNER_JOIN,
+ ExpressionUtils.EMPTY_CONDITION,
ExpressionUtils.EMPTY_CONDITION,
+ new DistributeHint(DistributeType.NONE), Optional.empty(),
logicalProperties,
+ leftGroupPlan, rightGroupPlan);
+
+ // simulate physical-tree logical prop recompute by resetting logical
properties on the join
+ AbstractPhysicalPlan processed = (AbstractPhysicalPlan)
join.resetLogicalProperties();
+ processed = (AbstractPhysicalPlan)
processed.copyStatsAndGroupIdFrom((AbstractPhysicalPlan) join);
+
+ Assertions.assertInstanceOf(PhysicalHashJoin.class, processed);
+
+ DataTrait.Builder builder = new DataTrait.Builder();
+ processed.computeUniform(builder);
+ DataTrait result = builder.build();
+
+ Assertions.assertTrue(result.isUniformAndNotNull(leftSlot));
+ Assertions.assertTrue(result.isUniformAndNotNull(rightSlot));
+ }
+
+ @Test
+ void testComputeUniformAfterRecomputeLogicalProperties_AsOfRightOuter() {
+ SlotReference leftSlot = new SlotReference(new ExprId(400), "l3",
IntegerType.INSTANCE, false,
+ Collections.emptyList());
+ SlotReference rightSlot = new SlotReference(new ExprId(401), "r3",
IntegerType.INSTANCE, false,
+ Collections.emptyList());
+ List<Slot> leftOutput = Lists.newArrayList(leftSlot);
+ List<Slot> rightOutput = Lists.newArrayList(rightSlot);
+
+ DataTrait.Builder rightBuilder = new DataTrait.Builder();
+ rightBuilder.addUniformSlot(rightSlot);
+ DataTrait rightTrait = rightBuilder.build();
+
+ LogicalProperties leftLogical = new LogicalProperties(() ->
leftOutput, () -> DataTrait.EMPTY_TRAIT);
+ LogicalProperties rightLogical = new LogicalProperties(() ->
rightOutput, () -> rightTrait);
+
+ IdGenerator<GroupId> idGenerator = GroupId.createGenerator();
+ GroupPlan leftGroupPlan = new GroupPlan(new
Group(idGenerator.getNextId(), leftLogical));
+ GroupPlan rightGroupPlan = new GroupPlan(new
Group(idGenerator.getNextId(), rightLogical));
+
+ PhysicalHashJoin<GroupPlan, GroupPlan> join = new
PhysicalHashJoin<>(JoinType.ASOF_RIGHT_OUTER_JOIN,
+ ExpressionUtils.EMPTY_CONDITION,
ExpressionUtils.EMPTY_CONDITION,
+ new DistributeHint(DistributeType.NONE), Optional.empty(),
logicalProperties,
+ leftGroupPlan, rightGroupPlan);
+
+ // simulate physical-tree logical prop recompute by resetting logical
properties on the join
+ AbstractPhysicalPlan processed = (AbstractPhysicalPlan)
join.resetLogicalProperties();
+ processed = (AbstractPhysicalPlan)
processed.copyStatsAndGroupIdFrom((AbstractPhysicalPlan) join);
+
+ Assertions.assertInstanceOf(PhysicalHashJoin.class, processed);
+
+ DataTrait.Builder builder = new DataTrait.Builder();
+ processed.computeUniform(builder);
+ DataTrait result = builder.build();
+
+ Assertions.assertTrue(result.isUniformAndNotNull(rightSlot));
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]