diff --git a/planner/core/physical_plan_test.go b/planner/core/physical_plan_test.go index c6932a22f50ea..b326bbfa29265 100644 --- a/planner/core/physical_plan_test.go +++ b/planner/core/physical_plan_test.go @@ -1693,3 +1693,22 @@ func (s *testPlanSuite) TestNthPlanHintWithExplain(c *C) { // hold in the future, you may need to modify this. tk.MustQuery("explain format = 'brief' select * from test.tt where a=1 and b=1").Check(testkit.Rows(output[1].Plan...)) } + +func (s *testPlanSuite) TestPossibleProperties(c *C) { + store, dom, err := newStoreWithBootstrap() + c.Assert(err, IsNil) + defer func() { + dom.Close() + store.Close() + }() + tk := testkit.NewTestKit(c, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists student, sc") + tk.MustExec("create table student(id int primary key auto_increment, name varchar(4) not null)") + tk.MustExec("create table sc(id int primary key auto_increment, student_id int not null, course_id int not null, score int not null)") + tk.MustExec("insert into student values (1,'s1'), (2,'s2')") + tk.MustExec("insert into sc (student_id, course_id, score) values (1,1,59), (1,2,57), (1,3,76), (2,1,99), (2,2,100), (2,3,100)") + tk.MustQuery("select /*+ stream_agg() */ a.id, avg(b.score) as afs from student a join sc b on a.id = b.student_id where b.score < 60 group by a.id having count(b.course_id) >= 2").Check(testkit.Rows( + "1 58.0000", + )) +} diff --git a/planner/core/property_cols_prune.go b/planner/core/property_cols_prune.go index c8296aa799283..91a9f34fb9017 100644 --- a/planner/core/property_cols_prune.go +++ b/planner/core/property_cols_prune.go @@ -165,13 +165,27 @@ func (p *LogicalProjection) PreparePossibleProperties(schema *expression.Schema, return childProperties } +func clonePossibleProperties(props [][]*expression.Column) [][]*expression.Column { + res := make([][]*expression.Column, len(props)) + for i, prop := range props { + clonedProp := make([]*expression.Column, len(prop)) + for j, col := range prop { + clonedProp[j] = col.Clone().(*expression.Column) + } + res[i] = clonedProp + } + return res +} + // PreparePossibleProperties implements LogicalPlan PreparePossibleProperties interface. func (p *LogicalJoin) PreparePossibleProperties(schema *expression.Schema, childrenProperties ...[][]*expression.Column) [][]*expression.Column { leftProperties := childrenProperties[0] rightProperties := childrenProperties[1] // TODO: We should consider properties propagation. - p.leftProperties = leftProperties - p.rightProperties = rightProperties + // Clone the Columns in the property before saving them, otherwise the upper Projection may + // modify them and lead to unexpected results. + p.leftProperties = clonePossibleProperties(leftProperties) + p.rightProperties = clonePossibleProperties(rightProperties) if p.JoinType == LeftOuterJoin || p.JoinType == LeftOuterSemiJoin { rightProperties = nil } else if p.JoinType == RightOuterJoin { @@ -200,13 +214,22 @@ func (la *LogicalAggregation) PreparePossibleProperties(schema *expression.Schem return nil } resultProperties := make([][]*expression.Column, 0, len(childProps)) + clonedProperties := make([][]*expression.Column, 0, len(childProps)) groupByCols := la.GetGroupByCols() for _, possibleChildProperty := range childProps { sortColOffsets := getMaxSortPrefix(possibleChildProperty, groupByCols) if len(sortColOffsets) == len(groupByCols) { - resultProperties = append(resultProperties, possibleChildProperty[:len(groupByCols)]) + prop := possibleChildProperty[:len(groupByCols)] + resultProperties = append(resultProperties, prop) + // Clone the Columns in the property before saving them, otherwise the upper Projection may + // modify them and lead to unexpected results. + clonedProp := make([]*expression.Column, len(prop)) + for i, col := range prop { + clonedProp[i] = col.Clone().(*expression.Column) + } + clonedProperties = append(clonedProperties, clonedProp) } } - la.possibleProperties = resultProperties - return la.possibleProperties + la.possibleProperties = clonedProperties + return resultProperties }