func reorderSort(sort *plan.Sort, missingCols []string) (sql.Node, error) {
	var expressions []sql.Expression
	switch child := sort.Child.(type) {
	case *plan.Project:
		expressions = child.Projections
	case *plan.GroupBy:
		expressions = child.SelectedExprs
	case *plan.Window:
		expressions = child.SelectExprs
	default:
		return nil, errSortPushdown.New(child)
	}
	var newExpressions = append([]sql.Expression{}, expressions...)
	for _, col := range missingCols {
		newExpressions = append(newExpressions, expression.NewUnresolvedColumn(col))
	}
	for i, e := range expressions {
		var name string
		if n, ok := e.(sql.Nameable); ok {
			name = n.Name()
		} else {
			name = e.String()
		}
		var table string
		if t, ok := e.(sql.Tableable); ok {
			table = t.Table()
		}
		expressions[i] = expression.NewGetFieldWithTable(
			i, e.Type(), table, name, e.IsNullable(),
		)
	}
	switch child := sort.Child.(type) {
	case *plan.Project:
		return plan.NewProject(
			expressions,
			plan.NewSort(
				sort.SortFields,
				plan.NewProject(newExpressions, child.Child),
			),
		), nil
	case *plan.GroupBy:
		return plan.NewProject(
			expressions,
			plan.NewSort(
				sort.SortFields,
				plan.NewGroupBy(newExpressions, child.GroupByExprs, child.Child),
			),
		), nil
	case *plan.Window:
		return plan.NewProject(
			expressions,
			plan.NewSort(
				sort.SortFields,
				plan.NewWindow(newExpressions, child.Child),
			),
		), nil
	default:
		return nil, errSortPushdown.New(child)
	}
}
reorderSort replaces the sort node by adding necessary missing columns to the child node and then reordering the
sort with its child:
sort(project(a)) becomes project(sort(project(a)))
sort(groupBy(a)) becomes project(sort(groupby(a)))
package analyzer
import (
	"strings"
	errors "gopkg.in/src-d/go-errors.v1"
	"github.com/dolthub/go-mysql-server/sql"
	"github.com/dolthub/go-mysql-server/sql/expression"
	"github.com/dolthub/go-mysql-server/sql/plan"
)
func pushdownSort(ctx *sql.Context, a *Analyzer, n sql.Node, scope *Scope) (sql.Node, error) {
	span, _ := ctx.Span("pushdownSort")
	defer span.Finish()
	return plan.TransformUp(n, func(n sql.Node) (sql.Node, error) {
		sort, ok := n.(*plan.Sort)
		if !ok {
			return n, nil
		}
		if !sort.Child.Resolved() {
			return n, nil
		}
		childAliases := aliasesDefinedInNode(sort.Child)
		var schemaCols []tableCol
		for _, col := range sort.Child.Schema() {
			schemaCols = append(schemaCols, tableCol{
				table: strings.ToLower(col.Source),
				col:   strings.ToLower(col.Name),
			})
		}
		var colsFromChild []string
		var missingCols []string
		for _, f := range sort.SortFields {
			ns := findExprNameables(f.Column)
			for _, n := range ns {
				name := strings.ToLower(n.Name())
				if stringContains(childAliases, name) {
					colsFromChild = append(colsFromChild, n.Name())
				} else if !tableColsContains(schemaCols, tableColFromNameable(n)) {
					missingCols = append(missingCols, n.Name())
				}
			}
		}
		if len(missingCols) == 0 {
			a.Log("no missing columns, skipping")
			return n, nil
		}
		if len(colsFromChild) == 0 {
			a.Log("pushing down sort, missing columns: %s", strings.Join(missingCols, ", "))
			return pushSortDown(sort)
		}
		a.Log("fixing sort dependencies, missing columns: %s", strings.Join(missingCols, ", "))
		return reorderSort(sort, missingCols)
	})
}