code: func (a *aggregationPushDownSolver) tryToPushDownAgg(aggFuncs []*aggregation.AggFuncDesc, gbyCols []*expression.Column, join *LogicalJoin, childIdx int) LogicalPlan {
  	child := join.children[childIdx]
  	if aggregation.IsAllFirstRow(aggFuncs) {
  		return child
  	}
  	// If the join is multiway-join, we forbid pushing down.
  	if _, ok := join.children[childIdx].(*LogicalJoin); ok {
  		return child
  	}
  	tmpSchema := expression.NewSchema(gbyCols...)
  	for _, key := range child.Schema().Keys {
  		if tmpSchema.ColumnsIndices(key) != nil {
  			return child
  		}
  	}
  	agg := a.makeNewAgg(join.ctx, aggFuncs, gbyCols)
  	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 {
  		agg.GroupByItems = []expression.Expression{&expression.Constant{
  			Value:   types.NewDatum(0),
  			RetType: types.NewFieldType(mysql.TypeLong)}}
  	}
  	if (childIdx == 0 && join.JoinType == RightOuterJoin) || (childIdx == 1 && join.JoinType == LeftOuterJoin) {
  		var existsDefaultValues bool
  		join.DefaultValues, existsDefaultValues = a.getDefaultValues(agg)
  		if !existsDefaultValues {
  			return child
  		}
  	}
  	return agg
  }
docs: // tryToPushDownAgg tries to push down an aggregate function into a join path. If all aggFuncs are first row, we won't
  // process it temporarily. If not, We will add additional group by columns and first row functions. We make a new aggregation operator.
  // If the pushed aggregation is grouped by unique key, it's no need to push it down.