plan: remove parents totally (#5454)

This commit is contained in:
Han Fei
2017-12-20 23:23:55 +08:00
committed by Jian Zhang
parent 6608dbdc2f
commit cebf2ebd2d
11 changed files with 51 additions and 123 deletions

View File

@ -213,7 +213,7 @@ func (a *aggregationOptimizer) tryToPushDownAgg(aggFuncs []aggregation.Aggregati
}
}
agg := a.makeNewAgg(aggFuncs, gbyCols)
setParentAndChildren(agg, child)
agg.SetChildren(child)
// If agg has no group-by item, it will return a default value, which may cause some bugs.
// So here we add a group-by item forcely.
if len(agg.GroupByItems) == 0 {
@ -304,11 +304,11 @@ func (a *aggregationOptimizer) pushAggCrossUnion(agg *LogicalAggregation, unionS
for _, key := range unionChild.Schema().Keys {
if tmpSchema.ColumnsIndices(key) != nil {
proj := a.convertAggToProj(newAgg, a.ctx)
setParentAndChildren(proj, unionChild)
proj.SetChildren(unionChild)
return proj
}
}
setParentAndChildren(newAgg, unionChild)
newAgg.SetChildren(unionChild)
return newAgg
}
@ -346,7 +346,7 @@ func (a *aggregationOptimizer) aggPushDown(p LogicalPlan) LogicalPlan {
} else {
lChild = a.tryToPushDownAgg(leftAggFuncs, leftGbyCols, join, 0)
}
setParentAndChildren(join, lChild, rChild)
join.SetChildren(lChild, rChild)
join.SetSchema(expression.MergeSchema(lChild.Schema(), rChild.Schema()))
join.buildKeyInfo()
proj := a.tryToEliminateAggregation(agg)
@ -369,7 +369,7 @@ func (a *aggregationOptimizer) aggPushDown(p LogicalPlan) LogicalPlan {
aggFunc.SetArgs(newArgs)
}
projChild := proj.children[0]
setParentAndChildren(agg, projChild)
agg.SetChildren(projChild)
} else if union, ok1 := child.(*LogicalUnionAll); ok1 {
var gbyCols []*expression.Column
gbyCols = expression.ExtractColumnsFromExpressions(gbyCols, agg.GroupByItems, nil)
@ -379,7 +379,7 @@ func (a *aggregationOptimizer) aggPushDown(p LogicalPlan) LogicalPlan {
newChild := a.pushAggCrossUnion(pushedAgg, union.schema, child.(LogicalPlan))
newChildren = append(newChildren, newChild)
}
setParentAndChildren(union, newChildren...)
union.SetChildren(newChildren...)
union.SetSchema(pushedAgg.schema)
}
}
@ -389,7 +389,7 @@ func (a *aggregationOptimizer) aggPushDown(p LogicalPlan) LogicalPlan {
newChild := a.aggPushDown(child.(LogicalPlan))
newChildren = append(newChildren, newChild)
}
setParentAndChildren(p, newChildren...)
p.SetChildren(newChildren...)
return p
}
@ -409,7 +409,7 @@ func (a *aggregationOptimizer) tryToEliminateAggregation(agg *LogicalAggregation
if coveredByUniqueKey {
// GroupByCols has unique key, so this aggregation can be removed.
proj := a.convertAggToProj(agg, a.ctx)
setParentAndChildren(proj, agg.children[0])
proj.SetChildren(agg.children[0])
return proj
}
return nil

View File

@ -91,8 +91,6 @@ func (s *decorrelateSolver) optimize(p LogicalPlan, _ context.Context) (LogicalP
if len(apply.corCols) == 0 {
// If the inner plan is non-correlated, the apply will be simplified to join.
join := &apply.LogicalJoin
innerPlan.SetParents(join)
outerPlan.SetParents(join)
join.self = join
p = join
} else if sel, ok := innerPlan.(*LogicalSelection); ok {
@ -104,12 +102,12 @@ func (s *decorrelateSolver) optimize(p LogicalPlan, _ context.Context) (LogicalP
}
apply.attachOnConds(newConds)
innerPlan = sel.children[0].(LogicalPlan)
setParentAndChildren(apply, outerPlan, innerPlan)
apply.SetChildren(outerPlan, innerPlan)
return s.optimize(p, nil)
} else if m, ok := innerPlan.(*LogicalMaxOneRow); ok {
if m.children[0].Schema().MaxOneRow {
innerPlan = m.children[0].(LogicalPlan)
setParentAndChildren(apply, outerPlan, innerPlan)
apply.SetChildren(outerPlan, innerPlan)
return s.optimize(p, nil)
}
} else if proj, ok := innerPlan.(*LogicalProjection); ok {
@ -118,17 +116,16 @@ func (s *decorrelateSolver) optimize(p LogicalPlan, _ context.Context) (LogicalP
}
apply.columnSubstitute(proj.Schema(), proj.Exprs)
innerPlan = proj.children[0].(LogicalPlan)
setParentAndChildren(apply, outerPlan, innerPlan)
apply.SetChildren(outerPlan, innerPlan)
if apply.JoinType != SemiJoin && apply.JoinType != LeftOuterSemiJoin && apply.JoinType != AntiSemiJoin && apply.JoinType != AntiLeftOuterSemiJoin {
proj.SetSchema(apply.Schema())
proj.Exprs = append(expression.Column2Exprs(outerPlan.Schema().Clone().Columns), proj.Exprs...)
apply.SetSchema(expression.MergeSchema(outerPlan.Schema(), innerPlan.Schema()))
proj.SetParents(apply.Parents()...)
np, err := s.optimize(p, nil)
if err != nil {
return nil, errors.Trace(err)
}
setParentAndChildren(proj, np)
proj.SetChildren(np)
return proj, nil
}
return s.optimize(p, nil)
@ -136,7 +133,7 @@ func (s *decorrelateSolver) optimize(p LogicalPlan, _ context.Context) (LogicalP
if apply.canPullUpAgg() && agg.canPullUp() {
innerPlan = agg.children[0].(LogicalPlan)
apply.JoinType = LeftOuterJoin
setParentAndChildren(apply, outerPlan, innerPlan)
apply.SetChildren(outerPlan, innerPlan)
agg.SetSchema(apply.Schema())
agg.GroupByItems = expression.Column2Exprs(outerPlan.Schema().Keys[0])
newAggFuncs := make([]aggregation.Aggregation, 0, apply.Schema().Len())
@ -147,12 +144,11 @@ func (s *decorrelateSolver) optimize(p LogicalPlan, _ context.Context) (LogicalP
newAggFuncs = append(newAggFuncs, agg.AggFuncs...)
agg.AggFuncs = newAggFuncs
apply.SetSchema(expression.MergeSchema(outerPlan.Schema(), innerPlan.Schema()))
agg.SetParents(apply.Parents()...)
np, err := s.optimize(p, nil)
if err != nil {
return nil, errors.Trace(err)
}
setParentAndChildren(agg, np)
agg.SetChildren(np)
agg.collectGroupByColumns()
return agg, nil
}
@ -166,6 +162,6 @@ func (s *decorrelateSolver) optimize(p LogicalPlan, _ context.Context) (LogicalP
}
newChildren = append(newChildren, np)
}
setParentAndChildren(p, newChildren...)
p.SetChildren(newChildren...)
return p, nil
}

View File

@ -69,19 +69,15 @@ func resolveExprAndReplace(origin expression.Expression, replace map[string]*exp
}
func doPhysicalProjectionElimination(p PhysicalPlan) PhysicalPlan {
children := make([]Plan, 0, len(p.Children()))
for _, child := range p.Children() {
newChild := doPhysicalProjectionElimination(child.(PhysicalPlan))
children = append(children, newChild)
for i, child := range p.Children() {
p.Children()[i] = doPhysicalProjectionElimination(child.(PhysicalPlan))
}
setParentAndChildren(p, children...)
proj, isProj := p.(*PhysicalProjection)
if !isProj || !canProjectionBeEliminatedStrict(proj) {
return p
}
child := p.Children()[0]
removePlan(p)
return child.(PhysicalPlan)
}
@ -114,18 +110,15 @@ func (pe *projectionEliminater) optimize(lp LogicalPlan, _ context.Context) (Log
// eliminate eliminates the redundant projection in a logical plan.
func (pe *projectionEliminater) eliminate(p LogicalPlan, replace map[string]*expression.Column, canEliminate bool) LogicalPlan {
proj, isProj := p.(*LogicalProjection)
children := make([]Plan, 0, len(p.Children()))
childFlag := canEliminate
if _, isUnion := p.(*LogicalUnionAll); isUnion {
childFlag = false
} else if _, isAgg := p.(*LogicalAggregation); isAgg || isProj {
childFlag = true
}
for _, child := range p.Children() {
children = append(children, pe.eliminate(child.(LogicalPlan), replace, childFlag))
for i, child := range p.Children() {
p.Children()[i] = pe.eliminate(child.(LogicalPlan), replace, childFlag)
}
setParentAndChildren(p, children...)
switch p.(type) {
case *LogicalSort, *LogicalTopN, *LogicalLimit, *LogicalSelection, *LogicalMaxOneRow, *LogicalLock:
@ -161,7 +154,6 @@ func (pe *projectionEliminater) eliminate(p LogicalPlan, replace map[string]*exp
for i, col := range proj.Schema().Columns {
replace[string(col.HashCode())] = exprs[i].(*expression.Column)
}
removePlan(p)
return p.Children()[0].(LogicalPlan)
}

View File

@ -362,7 +362,7 @@ func (er *expressionRewriter) handleOtherComparableSubq(lexpr, rexpr expression.
agg := LogicalAggregation{
AggFuncs: []aggregation.Aggregation{aggFunc},
}.init(er.ctx)
setParentAndChildren(agg, np)
agg.SetChildren(np)
aggCol0 := &expression.Column{
ColName: model.NewCIStr("agg_Col_0"),
FromID: agg.id,
@ -432,7 +432,7 @@ func (er *expressionRewriter) buildQuantifierPlan(agg *LogicalAggregation, cond,
IsAggOrSubq: true,
RetType: cond.GetType(),
})
setParentAndChildren(proj, er.p)
proj.SetChildren(er.p)
er.p = proj
}
@ -445,7 +445,7 @@ func (er *expressionRewriter) handleNEAny(lexpr, rexpr expression.Expression, np
agg := LogicalAggregation{
AggFuncs: []aggregation.Aggregation{firstRowFunc, countFunc},
}.init(er.ctx)
setParentAndChildren(agg, np)
agg.SetChildren(np)
firstRowResultCol := &expression.Column{
ColName: model.NewCIStr("col_firstRow"),
FromID: agg.id,
@ -473,7 +473,7 @@ func (er *expressionRewriter) handleEQAll(lexpr, rexpr expression.Expression, np
agg := LogicalAggregation{
AggFuncs: []aggregation.Aggregation{firstRowFunc, countFunc},
}.init(er.ctx)
setParentAndChildren(agg, np)
agg.SetChildren(np)
firstRowResultCol := &expression.Column{
ColName: model.NewCIStr("col_firstRow"),
FromID: agg.id,

View File

@ -190,7 +190,7 @@ func (e *joinReOrderSolver) newJoin(lChild, rChild LogicalPlan) *LogicalJoin {
reordered: true,
}.init(e.ctx)
join.SetSchema(expression.MergeSchema(lChild.Schema(), rChild.Schema()))
setParentAndChildren(join, lChild, rChild)
join.SetChildren(lChild, rChild)
return join
}

View File

@ -108,7 +108,7 @@ func (b *planBuilder) buildAggregation(p LogicalPlan, aggFuncList []*ast.Aggrega
agg.AggFuncs = append(agg.AggFuncs, newFunc)
schema.Append(col.Clone().(*expression.Column))
}
setParentAndChildren(agg, p)
agg.SetChildren(p)
agg.GroupByItems = gbyItems
agg.SetSchema(schema)
agg.collectGroupByColumns()
@ -242,7 +242,7 @@ func (b *planBuilder) buildJoin(join *ast.Join) LogicalPlan {
newSchema := expression.MergeSchema(leftPlan.Schema(), rightPlan.Schema())
joinPlan := LogicalJoin{}.init(b.ctx)
setParentAndChildren(joinPlan, leftPlan, rightPlan)
joinPlan.SetChildren(leftPlan, rightPlan)
joinPlan.SetSchema(newSchema)
// Merge sub join's redundantSchema into this join plan. When handle query like
@ -444,7 +444,7 @@ func (b *planBuilder) buildSelection(p LogicalPlan, where ast.ExprNode, AggMappe
}
selection.Conditions = expressions
selection.SetSchema(p.Schema().Clone())
setParentAndChildren(selection, p)
selection.SetChildren(p)
return selection
}
@ -554,7 +554,7 @@ func (b *planBuilder) buildProjection(p LogicalPlan, fields []*ast.SelectField,
}
}
proj.SetSchema(schema)
setParentAndChildren(proj, p)
proj.SetChildren(p)
return proj, oldLen
}
@ -569,7 +569,7 @@ func (b *planBuilder) buildDistinct(child LogicalPlan, length int) LogicalPlan {
for _, col := range child.Schema().Columns {
agg.AggFuncs = append(agg.AggFuncs, aggregation.NewAggFunction(ast.AggFuncFirstRow, []expression.Expression{col}, false))
}
setParentAndChildren(agg, child)
agg.SetChildren(child)
agg.SetSchema(child.Schema().Clone())
return agg
}
@ -607,11 +607,11 @@ func (b *planBuilder) buildProjection4Union(u *LogicalUnionAll) {
for _, col := range proj.schema.Columns {
col.FromID = proj.ID()
}
setParentAndChildren(proj, u.children[childID])
proj.SetChildren(u.children[childID])
u.children[childID] = proj
}
}
setParentAndChildren(u, u.children...)
u.SetChildren(u.children...)
}
func (b *planBuilder) buildUnion(union *ast.UnionStmt) LogicalPlan {
@ -637,11 +637,10 @@ func (b *planBuilder) buildUnion(union *ast.UnionStmt) LogicalPlan {
col.FromID = proj.ID()
}
proj.SetSchema(schema)
setParentAndChildren(proj, sel)
proj.SetChildren(sel)
sel = proj
u.children[i] = proj
}
sel.SetParents(u)
}
// infer union type
@ -710,7 +709,7 @@ func (b *planBuilder) buildSort(p LogicalPlan, byItems []*ast.ByItem, aggMapper
exprs = append(exprs, &ByItems{Expr: it, Desc: item.Desc})
}
sort.ByItems = exprs
setParentAndChildren(sort, p)
sort.SetChildren(p)
sort.SetSchema(p.Schema().Clone())
return sort
}
@ -761,7 +760,7 @@ func (b *planBuilder) buildLimit(src LogicalPlan, limit *ast.Limit) LogicalPlan
Offset: offset,
Count: count,
}.init(b.ctx)
setParentAndChildren(li, src)
li.SetChildren(src)
li.SetSchema(src.Schema().Clone())
return li
}
@ -1551,7 +1550,7 @@ func (b *planBuilder) buildSelect(sel *ast.SelectStmt) LogicalPlan {
sel.Fields.Fields = originalFields
if oldLen != p.Schema().Len() {
proj := LogicalProjection{Exprs: expression.Column2Exprs(p.Schema().Columns[:oldLen])}.init(b.ctx)
setParentAndChildren(proj, p)
proj.SetChildren(p)
schema := expression.NewSchema(p.Schema().Clone().Columns[:oldLen]...)
for _, col := range schema.Columns {
col.FromID = proj.ID()
@ -1652,12 +1651,12 @@ func (b *planBuilder) buildDataSource(tn *ast.TableName) LogicalPlan {
if b.ctx.Txn() != nil && !b.ctx.Txn().IsReadOnly() {
us := LogicalUnionScan{}.init(b.ctx)
us.SetSchema(ds.Schema().Clone())
setParentAndChildren(us, result)
us.SetChildren(result)
result = us
}
proj := b.projectVirtualColumns(ds, columns)
if proj != nil {
setParentAndChildren(proj, result)
proj.SetChildren(result)
result = proj
}
return result
@ -1715,7 +1714,7 @@ func (b *planBuilder) buildApplyWithJoinType(outerPlan, innerPlan LogicalPlan, t
b.optFlag = b.optFlag | flagBuildKeyInfo
b.optFlag = b.optFlag | flagDecorrelate
ap := LogicalApply{LogicalJoin: LogicalJoin{JoinType: tp}}.init(b.ctx)
setParentAndChildren(ap, outerPlan, innerPlan)
ap.SetChildren(outerPlan, innerPlan)
ap.SetSchema(expression.MergeSchema(outerPlan.Schema(), innerPlan.Schema()))
for i := outerPlan.Schema().Len(); i < ap.Schema().Len(); i++ {
ap.schema.Columns[i].IsAggOrSubq = true
@ -1732,8 +1731,6 @@ func (b *planBuilder) buildSemiApply(outerPlan, innerPlan LogicalPlan, condition
ap := &LogicalApply{LogicalJoin: *join}
ap.tp = TypeApply
ap.self = ap
ap.children[0].SetParents(ap)
ap.children[1].SetParents(ap)
return ap
}
@ -1745,20 +1742,18 @@ out:
// e.g. exists(select count(*) from t order by a) is equal to exists t.
case *LogicalProjection, *LogicalSort:
p = p.Children()[0].(LogicalPlan)
p.SetParents()
case *LogicalAggregation:
if len(plan.GroupByItems) == 0 {
p = b.buildTableDual()
break out
}
p = p.Children()[0].(LogicalPlan)
p.SetParents()
default:
break out
}
}
exists := LogicalExists{}.init(b.ctx)
setParentAndChildren(exists, p)
exists.SetChildren(p)
newCol := &expression.Column{
FromID: exists.id,
RetType: types.NewFieldType(mysql.TypeTiny),
@ -1769,7 +1764,7 @@ out:
func (b *planBuilder) buildMaxOneRow(p LogicalPlan) LogicalPlan {
maxOneRow := LogicalMaxOneRow{}.init(b.ctx)
setParentAndChildren(maxOneRow, p)
maxOneRow.SetChildren(p)
maxOneRow.SetSchema(p.Schema().Clone())
return maxOneRow
}
@ -1779,7 +1774,7 @@ func (b *planBuilder) buildSemiJoin(outerPlan, innerPlan LogicalPlan, onConditio
for i, expr := range onCondition {
onCondition[i] = expr.Decorrelate(outerPlan.Schema())
}
setParentAndChildren(joinPlan, outerPlan, innerPlan)
joinPlan.SetChildren(outerPlan, innerPlan)
joinPlan.attachOnConds(onCondition)
if asScalar {
newSchema := outerPlan.Schema().Clone()

View File

@ -28,8 +28,6 @@ import (
// It is created from ast.Node first, then optimized by the optimizer,
// finally used by the executor to create a Cursor which executes the statement.
type Plan interface {
// Get all the parents.
Parents() []Plan
// Get all the children.
Children() []Plan
// Set the schema.
@ -40,8 +38,6 @@ type Plan interface {
ID() int
// Get the ID in explain statement
ExplainID() string
// SetParents sets the parents for the plan.
SetParents(...Plan)
// SetChildren sets the children for the plan.
SetChildren(...Plan)
// replaceExprColumns replace all the column reference in the plan's expression node.
@ -308,7 +304,6 @@ func (p *baseLogicalPlan) PruneColumns(parentUsedCols []*expression.Column) {
// basePlan implements base Plan interface.
// Should be used as embedded struct in Plan implementations.
type basePlan struct {
parents []Plan
children []Plan
schema *expression.Schema
@ -340,21 +335,11 @@ func (p *basePlan) Schema() *expression.Schema {
return p.schema
}
// Parents implements Plan Parents interface.
func (p *basePlan) Parents() []Plan {
return p.parents
}
// Children implements Plan Children interface.
func (p *basePlan) Children() []Plan {
return p.children
}
// SetParents implements Plan SetParents interface.
func (p *basePlan) SetParents(pars ...Plan) {
p.parents = pars
}
// SetChildren implements Plan SetChildren interface.
func (p *basePlan) SetChildren(children ...Plan) {
p.children = children

View File

@ -237,8 +237,7 @@ func (b *planBuilder) buildDo(v *ast.DoStmt) Plan {
}
dual.SetSchema(expression.NewSchema())
p := LogicalProjection{Exprs: exprs}.init(b.ctx)
setParentAndChildren(p, dual)
p.self = p
p.SetChildren(dual)
p.SetSchema(expression.NewSchema())
return p
}
@ -381,7 +380,7 @@ func findIndexByName(indices []*model.IndexInfo, name model.CIStr) *model.IndexI
func (b *planBuilder) buildSelectLock(src Plan, lock ast.SelectLockType) *LogicalLock {
selectLock := LogicalLock{Lock: lock}.init(b.ctx)
setParentAndChildren(selectLock, src)
selectLock.SetChildren(src)
selectLock.SetSchema(src.Schema())
return selectLock
}

View File

@ -209,7 +209,7 @@ func (p *LogicalJoin) getProj(idx int) *LogicalProjection {
proj.Exprs = append(proj.Exprs, col.Clone())
}
proj.SetSchema(child.Schema().Clone())
setParentAndChildren(proj, child)
proj.SetChildren(child)
p.children[idx] = proj
return proj
}

View File

@ -30,7 +30,6 @@ func (s *baseLogicalPlan) pushDownTopN(topN *LogicalTopN) LogicalPlan {
p := s.self
for i, child := range p.Children() {
p.Children()[i] = child.(LogicalPlan).pushDownTopN(nil)
p.Children()[i].SetParents(p)
}
if topN != nil {
return topN.setChild(p, false)
@ -49,12 +48,12 @@ func (s *LogicalTopN) setChild(p LogicalPlan, eliminable bool) LogicalPlan {
Offset: s.Offset,
partial: s.partial,
}.init(s.ctx)
setParentAndChildren(limit, p)
limit.SetChildren(p)
limit.SetSchema(p.Schema().Clone())
return limit
}
// Then s must be topN.
setParentAndChildren(s, p)
s.SetChildren(p)
s.SetSchema(p.Schema().Clone())
return s
}
@ -94,7 +93,6 @@ func (p *LogicalUnionAll) pushDownTopN(topN *LogicalTopN) LogicalPlan {
}
}
p.children[i] = child.(LogicalPlan).pushDownTopN(newTopN)
p.children[i].SetParents(p)
}
if topN != nil {
return topN.setChild(p, true)
@ -108,8 +106,7 @@ func (p *LogicalProjection) pushDownTopN(topN *LogicalTopN) LogicalPlan {
by.Expr = expression.ColumnSubstitute(by.Expr, p.schema, p.Exprs)
}
}
child := p.children[0].(LogicalPlan).pushDownTopN(topN)
setParentAndChildren(p, child)
p.children[0] = p.children[0].(LogicalPlan).pushDownTopN(topN)
return p
}
@ -142,20 +139,18 @@ func (p *LogicalJoin) pushDownTopNToChild(topN *LogicalTopN, idx int) LogicalPla
}
func (p *LogicalJoin) pushDownTopN(topN *LogicalTopN) LogicalPlan {
var leftChild, rightChild LogicalPlan
switch p.JoinType {
case LeftOuterJoin, LeftOuterSemiJoin, AntiLeftOuterSemiJoin:
leftChild = p.pushDownTopNToChild(topN, 0)
rightChild = p.children[1].(LogicalPlan).pushDownTopN(nil)
p.children[0] = p.pushDownTopNToChild(topN, 0)
p.children[1] = p.children[1].(LogicalPlan).pushDownTopN(nil)
case RightOuterJoin:
leftChild = p.children[0].(LogicalPlan).pushDownTopN(nil)
rightChild = p.pushDownTopNToChild(topN, 1)
p.children[0] = p.children[0].(LogicalPlan).pushDownTopN(nil)
p.children[1] = p.pushDownTopNToChild(topN, 1)
default:
return p.baseLogicalPlan.pushDownTopN(topN)
}
// The LogicalJoin may be also a LogicalApply. So we must use self to set parents.
self := p.self.(LogicalPlan)
setParentAndChildren(self, leftChild, rightChild)
if topN != nil {
return topN.setChild(self, true)
}

View File

@ -45,37 +45,3 @@ func (a *AggregateFuncExtractor) Leave(n ast.Node) (ast.Node, bool) {
}
return n, true
}
// replaceChild replaces p's child with some plan else.
func replaceChild(p, child, replace Plan) {
for i, ch := range p.Children() {
if ch.ID() == child.ID() {
p.Children()[i] = replace
}
}
}
// setParentAndChildren sets parent and children relationship.
func setParentAndChildren(parent Plan, children ...Plan) {
if children == nil || parent == nil {
return
}
for _, child := range children {
child.SetParents(parent)
}
parent.SetChildren(children...)
}
// removePlan removes a plan from its parent and child.
func removePlan(p Plan) {
parents := p.Parents()
children := p.Children()
if len(parents) == 0 {
child := children[0]
child.SetParents()
return
}
parent, child := parents[0], children[0]
replaceChild(parent, p, child)
child.SetParents(parent)
}