func moveJoinConditionsToFilter(ctx *sql.Context, a *Analyzer, n sql.Node, scope *Scope) (sql.Node, error) {
	if !n.Resolved() {
		return n, nil
	}
	var nonJoinFilters []sql.Expression
	var topJoin sql.Node
	node, err := plan.TransformUp(n, func(n sql.Node) (sql.Node, error) {
		join, ok := n.(*plan.InnerJoin)
		if !ok {
			return n, nil
		}
		leftSources := nodeSources(join.Left())
		rightSources := nodeSources(join.Right())
		filtersMoved := 0
		var condFilters []sql.Expression
		for _, e := range splitConjunction(join.Cond) {
			sources := expressionSources(e)
			belongsToLeftTable := containsSources(leftSources, sources)
			belongsToRightTable := containsSources(rightSources, sources)
			if belongsToLeftTable || belongsToRightTable {
				nonJoinFilters = append(nonJoinFilters, e)
				filtersMoved++
			} else {
				condFilters = append(condFilters, e)
			}
		}
		if filtersMoved == 0 {
			topJoin = n
			return topJoin, nil
		}
		if len(condFilters) > 0 {
			var err error
			topJoin, err = join.WithExpressions(expression.JoinAnd(condFilters...))
			if err != nil {
				return nil, err
			}
			return topJoin, nil
		}
		topJoin = plan.NewCrossJoin(join.Left(), join.Right())
		return topJoin, nil
	})
	if err != nil {
		return nil, err
	}
	if len(nonJoinFilters) == 0 {
		return node, nil
	}
	selector := func(c plan.TransformContext) bool {
		switch c.Parent.(type) {
		case *plan.Filter:
			return false
		}
		return c.Parent != topJoin
	}
	return plan.TransformUpCtx(node, selector, func(c plan.TransformContext) (sql.Node, error) {
		switch node := c.Node.(type) {
		case *plan.Filter:
			return plan.NewFilter(
				expression.JoinAnd(append([]sql.Expression{node.Expression}, nonJoinFilters...)...),
				node.Child), nil
		case *plan.InnerJoin, *plan.CrossJoin:
			return plan.NewFilter(
				expression.JoinAnd(nonJoinFilters...),
				node), nil
		default:
			return node, nil
		}
	})
}
moveJoinConditionsToFilter looks for expressions in a join condition that reference only tables in the left or right
side of the join, and move those conditions to a new Filter node instead. If the join condition is empty after these
moves, the join is converted to a CrossJoin.
package analyzer
import (
	"github.com/dolthub/go-mysql-server/sql"
	"github.com/dolthub/go-mysql-server/sql/expression"
	"github.com/dolthub/go-mysql-server/sql/plan"
)
func eraseProjection(ctx *sql.Context, a *Analyzer, node sql.Node, scope *Scope) (sql.Node, error) {
	span, _ := ctx.Span("erase_projection")
	defer span.Finish()
	if !node.Resolved() {
		return node, nil
	}
	return plan.TransformUp(node, func(node sql.Node) (sql.Node, error) {
		project, ok := node.(*plan.Project)
		if ok && project.Schema().Equals(project.Child.Schema()) {
			a.Log("project erased")
			return project.Child, nil
		}
		return node, nil
	})
}
func optimizeDistinct(ctx *sql.Context, a *Analyzer, node sql.Node, scope *Scope) (sql.Node, error) {
	span, _ := ctx.Span("optimize_distinct")
	defer span.Finish()
	if n, ok := node.(*plan.Distinct); ok {
		var sortField *expression.GetField
		plan.Inspect(n, func(node sql.Node) bool {
			if sort, ok := node.(*plan.Sort); ok && sortField == nil {
				if col, ok := sort.SortFields[0].Column.(*expression.GetField); ok {
					sortField = col
				}
				return false
			}
			return true
		})
		if sortField != nil && n.Schema().Contains(sortField.Name(), sortField.Table()) {
			a.Log("distinct optimized for ordered output")
			return plan.NewOrderedDistinct(n.Child), nil
		}
	}
	return node, nil
}