code: func (p *Join) PruneColumns(parentUsedCols []*expression.Column) {
  	for _, eqCond := range p.EqualConditions {
  		parentUsedCols = append(parentUsedCols, expression.ExtractColumns(eqCond)...)
  	}
  	for _, leftCond := range p.LeftConditions {
  		parentUsedCols = append(parentUsedCols, expression.ExtractColumns(leftCond)...)
  	}
  	for _, rightCond := range p.RightConditions {
  		parentUsedCols = append(parentUsedCols, expression.ExtractColumns(rightCond)...)
  	}
  	for _, otherCond := range p.OtherConditions {
  		parentUsedCols = append(parentUsedCols, expression.ExtractColumns(otherCond)...)
  	}
  	lChild := p.GetChildByIndex(0).(LogicalPlan)
  	rChild := p.GetChildByIndex(1).(LogicalPlan)
  	var leftCols, rightCols []*expression.Column
  	for _, col := range parentUsedCols {
  		if lChild.GetSchema().GetColumnIndex(col) != -1 {
  			leftCols = append(leftCols, col)
  		} else if rChild.GetSchema().GetColumnIndex(col) != -1 {
  			rightCols = append(rightCols, col)
  		}
  	}
  	lChild.PruneColumns(leftCols)
  	rChild.PruneColumns(rightCols)
  	composedSchema := expression.MergeSchema(lChild.GetSchema(), rChild.GetSchema())
  	if p.JoinType == SemiJoin {
  		p.schema = lChild.GetSchema().Clone()
  	} else if p.JoinType == LeftOuterSemiJoin {
  		joinCol := p.schema.Columns[len(p.schema.Columns)-1]
  		p.schema = lChild.GetSchema().Clone()
  		p.schema.Append(joinCol)
  	} else {
  		p.schema = composedSchema
  	}
  }
docs: // PruneColumns implements LogicalPlan interface.