From 114ce56ea7bd5ce3347799dbb5991b738e538246 Mon Sep 17 00:00:00 2001 From: lichi Date: Thu, 23 Apr 2026 10:22:46 +0800 Subject: [PATCH 1/3] [fix](asof_join)PhysicalHashJoin's computeUniform method should process asof join properly --- .../doris/nereids/trees/plans/physical/PhysicalHashJoin.java | 4 ++++ 1 file changed, 4 insertions(+) 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 7b18b01865e21a..a8376464f11fa4 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 void computeUniform(DataTrait.Builder builder) { } 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 void computeUniform(DataTrait.Builder builder) { 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; From b89b2d79a0d9cedbad0ec9b95c1ebb433b7a145b Mon Sep 17 00:00:00 2001 From: lichi Date: Thu, 23 Apr 2026 20:29:28 +0800 Subject: [PATCH 2/3] add test --- .../ChildOutputPropertyDeriverTest.java | 170 +++++++++++++++++- 1 file changed, 169 insertions(+), 1 deletion(-) 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 c2fa770890f659..12ce4810de5e0e 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 @@ -21,10 +21,12 @@ import org.apache.doris.catalog.Env; import org.apache.doris.common.FeConstants; import org.apache.doris.common.IdGenerator; +import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.hint.DistributeHint; import org.apache.doris.nereids.memo.Group; import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.memo.GroupId; +import org.apache.doris.nereids.processor.post.RecomputeLogicalPropertiesProcessor; import org.apache.doris.nereids.properties.DistributionSpecHash.ShuffleType; import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.AssertNumRowsElement; @@ -42,6 +44,7 @@ import org.apache.doris.nereids.trees.plans.GroupPlan; import org.apache.doris.nereids.trees.plans.JoinType; import org.apache.doris.nereids.trees.plans.LimitPhase; +import org.apache.doris.nereids.trees.plans.Plan; 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; @@ -590,7 +593,7 @@ void testBroadcastJoin() { PhysicalHashJoin 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 +974,169 @@ void testComputeProjectOutputProperties() { 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 leftOutput = Lists.newArrayList(leftSlot); + List 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 idGenerator = GroupId.createGenerator(); + GroupPlan leftGroupPlan = new GroupPlan(new Group(idGenerator.getNextId(), leftLogical)); + GroupPlan rightGroupPlan = new GroupPlan(new Group(idGenerator.getNextId(), rightLogical)); + + PhysicalHashJoin join = new PhysicalHashJoin<>(JoinType.ASOF_LEFT_OUTER_JOIN, + ExpressionUtils.EMPTY_CONDITION, ExpressionUtils.EMPTY_CONDITION, + new DistributeHint(DistributeType.NONE), Optional.empty(), logicalProperties, + leftGroupPlan, rightGroupPlan); + + // run RecomputeLogicalPropertiesProcessor to simulate physical-tree logical prop recompute + RecomputeLogicalPropertiesProcessor processor = new RecomputeLogicalPropertiesProcessor(); + CascadesContext ctx = CascadesContext.initTempContext(); + Plan processed = processor.processRoot(join, ctx); + + 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 leftOutput = Lists.newArrayList(leftSlot); + List 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 idGenerator = GroupId.createGenerator(); + GroupPlan leftGroupPlan = new GroupPlan(new Group(idGenerator.getNextId(), leftLogical)); + GroupPlan rightGroupPlan = new GroupPlan(new Group(idGenerator.getNextId(), rightLogical)); + + PhysicalHashJoin join = new PhysicalHashJoin<>(JoinType.ASOF_LEFT_INNER_JOIN, + ExpressionUtils.EMPTY_CONDITION, ExpressionUtils.EMPTY_CONDITION, + new DistributeHint(DistributeType.NONE), Optional.empty(), logicalProperties, + leftGroupPlan, rightGroupPlan); + + RecomputeLogicalPropertiesProcessor processor = new RecomputeLogicalPropertiesProcessor(); + CascadesContext ctx = CascadesContext.initTempContext(); + Plan processed = processor.processRoot(join, ctx); + + 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 leftOutput = Lists.newArrayList(leftSlot); + List 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 idGenerator = GroupId.createGenerator(); + GroupPlan leftGroupPlan = new GroupPlan(new Group(idGenerator.getNextId(), leftLogical)); + GroupPlan rightGroupPlan = new GroupPlan(new Group(idGenerator.getNextId(), rightLogical)); + + PhysicalHashJoin join = new PhysicalHashJoin<>(JoinType.ASOF_RIGHT_INNER_JOIN, + ExpressionUtils.EMPTY_CONDITION, ExpressionUtils.EMPTY_CONDITION, + new DistributeHint(DistributeType.NONE), Optional.empty(), logicalProperties, + leftGroupPlan, rightGroupPlan); + + RecomputeLogicalPropertiesProcessor processor = new RecomputeLogicalPropertiesProcessor(); + CascadesContext ctx = CascadesContext.initTempContext(); + Plan processed = processor.processRoot(join, ctx); + + 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 leftOutput = Lists.newArrayList(leftSlot); + List 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 idGenerator = GroupId.createGenerator(); + GroupPlan leftGroupPlan = new GroupPlan(new Group(idGenerator.getNextId(), leftLogical)); + GroupPlan rightGroupPlan = new GroupPlan(new Group(idGenerator.getNextId(), rightLogical)); + + PhysicalHashJoin join = new PhysicalHashJoin<>(JoinType.ASOF_RIGHT_OUTER_JOIN, + ExpressionUtils.EMPTY_CONDITION, ExpressionUtils.EMPTY_CONDITION, + new DistributeHint(DistributeType.NONE), Optional.empty(), logicalProperties, + leftGroupPlan, rightGroupPlan); + + RecomputeLogicalPropertiesProcessor processor = new RecomputeLogicalPropertiesProcessor(); + CascadesContext ctx = CascadesContext.initTempContext(); + Plan processed = processor.processRoot(join, ctx); + + Assertions.assertInstanceOf(PhysicalHashJoin.class, processed); + + DataTrait.Builder builder = new DataTrait.Builder(); + processed.computeUniform(builder); + DataTrait result = builder.build(); + + Assertions.assertTrue(result.isUniformAndNotNull(rightSlot)); + } } From 77042b65705badb955908385e2105c64b92a2853 Mon Sep 17 00:00:00 2001 From: lichi Date: Thu, 23 Apr 2026 21:04:49 +0800 Subject: [PATCH 3/3] fix comments --- .../ChildOutputPropertyDeriverTest.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) 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 12ce4810de5e0e..fd23de7a2f46d6 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 @@ -21,12 +21,10 @@ import org.apache.doris.catalog.Env; import org.apache.doris.common.FeConstants; import org.apache.doris.common.IdGenerator; -import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.hint.DistributeHint; import org.apache.doris.nereids.memo.Group; import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.memo.GroupId; -import org.apache.doris.nereids.processor.post.RecomputeLogicalPropertiesProcessor; import org.apache.doris.nereids.properties.DistributionSpecHash.ShuffleType; import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.AssertNumRowsElement; @@ -44,10 +42,10 @@ import org.apache.doris.nereids.trees.plans.GroupPlan; import org.apache.doris.nereids.trees.plans.JoinType; import org.apache.doris.nereids.trees.plans.LimitPhase; -import org.apache.doris.nereids.trees.plans.Plan; 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; @@ -1001,10 +999,9 @@ void testComputeUniformAfterRecomputeLogicalProperties() { new DistributeHint(DistributeType.NONE), Optional.empty(), logicalProperties, leftGroupPlan, rightGroupPlan); - // run RecomputeLogicalPropertiesProcessor to simulate physical-tree logical prop recompute - RecomputeLogicalPropertiesProcessor processor = new RecomputeLogicalPropertiesProcessor(); - CascadesContext ctx = CascadesContext.initTempContext(); - Plan processed = processor.processRoot(join, ctx); + // 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); @@ -1045,9 +1042,9 @@ void testComputeUniformAfterRecomputeLogicalProperties_AsOfLeftInner() { new DistributeHint(DistributeType.NONE), Optional.empty(), logicalProperties, leftGroupPlan, rightGroupPlan); - RecomputeLogicalPropertiesProcessor processor = new RecomputeLogicalPropertiesProcessor(); - CascadesContext ctx = CascadesContext.initTempContext(); - Plan processed = processor.processRoot(join, ctx); + // 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); @@ -1088,9 +1085,9 @@ void testComputeUniformAfterRecomputeLogicalProperties_AsOfRightInner() { new DistributeHint(DistributeType.NONE), Optional.empty(), logicalProperties, leftGroupPlan, rightGroupPlan); - RecomputeLogicalPropertiesProcessor processor = new RecomputeLogicalPropertiesProcessor(); - CascadesContext ctx = CascadesContext.initTempContext(); - Plan processed = processor.processRoot(join, ctx); + // 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); @@ -1127,9 +1124,9 @@ void testComputeUniformAfterRecomputeLogicalProperties_AsOfRightOuter() { new DistributeHint(DistributeType.NONE), Optional.empty(), logicalProperties, leftGroupPlan, rightGroupPlan); - RecomputeLogicalPropertiesProcessor processor = new RecomputeLogicalPropertiesProcessor(); - CascadesContext ctx = CascadesContext.initTempContext(); - Plan processed = processor.processRoot(join, ctx); + // 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);