func indexHasExpression(indexLookups indexLookupsByTable, expr sql.Expression) bool {
	getField := extractGetField(expr)
	if getField == nil {
		return false
	}
	for _, indexLookup := range indexLookups {
		for _, idx := range indexLookup.indexes {
			for _, exprStr := range idx.Expressions() {
				if exprStr == getField.String() {
					return true
				}
			}
		}
	}
	return false
}
Returns whether the given index contains the given expression as one of its terms. The expression should be
normalized (table names unaliased) to ensure matching the index's declaration.
package analyzer
import (
	"reflect"
	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"
)
var errInvalidInRightEvaluation = errors.NewKind("expecting evaluation of IN expression right hand side to be a tuple, but it is %T")
type indexLookup struct {
	exprs   []sql.Expression
	lookup  sql.IndexLookup
	indexes []sql.Index
}
type indexLookupsByTable map[string]*indexLookup
func getIndexesByTable(ctx *sql.Context, a *Analyzer, node sql.Node, scope *Scope) (indexLookupsByTable, error) {
	indexSpan, _ := ctx.Span("getIndexesByTable")
	defer indexSpan.Finish()
	tableAliases, err := getTableAliases(node, scope)
	if err != nil {
		return nil, err
	}
	var indexes indexLookupsByTable
	cont := true
	var errInAnalysis error
	plan.Inspect(node, func(node sql.Node) bool {
		if !cont || errInAnalysis != nil {
			return false
		}
		filter, ok := node.(*plan.Filter)
		if !ok {
			return true
		}
		indexAnalyzer, err := getIndexesForNode(ctx, a, node)
		if err != nil {
			errInAnalysis = err
			return false
		}
		defer indexAnalyzer.releaseUsedIndexes()
		var result indexLookupsByTable
		filterExpression := convertIsNullForIndexes(ctx, filter.Expression)
		result, err = getIndexes(ctx, a, indexAnalyzer, filterExpression, tableAliases)
		if err != nil {
			return false
		}
		if !canMergeIndexLookups(indexes, result) {
			indexes = nil
			cont = false
			return false
		}
		indexes, err = indexesIntersection(indexes, result)
		if err != nil {
			errInAnalysis = err
			return false
		}
		return true
	})
	if errInAnalysis != nil {
		return nil, errInAnalysis
	}
	return indexes, nil
}
func getIndexes(
	ctx *sql.Context,
	a *Analyzer,
	ia *indexAnalyzer,
	e sql.Expression,
	tableAliases TableAliases,
) (indexLookupsByTable, error) {
	var result = make(indexLookupsByTable)
	switch e := e.(type) {
	case *expression.Or:
		if len(findTables(e)) > 1 {
			return nil, nil
		}
		leftIndexes, err := getIndexes(ctx, a, ia, e.Left, tableAliases)
		if err != nil {
			return nil, err
		}
		rightIndexes, err := getIndexes(ctx, a, ia, e.Right, tableAliases)
		if err != nil {
			return nil, err
		}
		for table, leftIdx := range leftIndexes {
			result[table] = leftIdx
		}
		for table, leftIdx := range leftIndexes {
			foundRightIdx := false
			if rightIdx, ok := rightIndexes[table]; ok {
				if canMergeIndexes(leftIdx.lookup, rightIdx.lookup) {
					leftIdx.lookup, err = leftIdx.lookup.(sql.MergeableIndexLookup).Union(rightIdx.lookup)
					if err != nil {
						return nil, err
					}
					leftIdx.indexes = append(leftIdx.indexes, rightIdx.indexes...)
					result[table] = leftIdx
					foundRightIdx = true
					delete(rightIndexes, table)
				} else {
					return nil, nil
				}
			}
			if !foundRightIdx {
				return nil, nil
			}
		}
		if len(rightIndexes) > 0 {
			return nil, nil
		}
	case *expression.InTuple:
		if !isEvaluable(e.Left()) && isEvaluable(e.Right()) {
			gf := extractGetField(e.Left())
			if gf == nil {
				return nil, nil
			}
			idx := ia.IndexByExpression(ctx, ctx.GetCurrentDatabase(), gf.Table(), normalizeExpressions(ctx, tableAliases, e.Left())...)
			if idx != nil {
				value, err := e.Right().Eval(sql.NewEmptyContext(), nil)
				if err != nil {
					return nil, err
				}
				values, ok := value.([]interface{})
				if !ok {
					return nil, errInvalidInRightEvaluation.New(value)
				}
				lookup, errLookup := idx.Get(values[0])
				if errLookup != nil {
					return nil, err
				}
				tounion := make([]sql.IndexLookup, 0, len(values)-1)
				for _, v := range values[1:] {
					lookup2, errLookup := idx.Get(v)
					if errLookup != nil {
						return nil, err
					}
					if !canMergeIndexes(lookup, lookup2) {
						return nil, nil
					}
					tounion = append(tounion, lookup2)
				}
				lookup, err = lookup.(sql.MergeableIndexLookup).Union(tounion...)
				if err != nil {
					return nil, err
				}
				getField := extractGetField(e.Left())
				if getField == nil {
					return result, nil
				}
				result[getField.Table()] = &indexLookup{
					exprs:   []sql.Expression{e},
					indexes: []sql.Index{idx},
					lookup:  lookup,
				}
			}
		}
	case *expression.Equals,
		*expression.NullSafeEquals,
		*expression.LessThan,
		*expression.GreaterThan,
		*expression.LessThanOrEqual,
		*expression.GreaterThanOrEqual:
		lookup, err := getComparisonIndexLookup(ctx, a, ia, e.(expression.Comparer), tableAliases)
		if err != nil || lookup == nil {
			return result, err
		}
		getField := extractGetField(e)
		if getField == nil {
			return result, nil
		}
		result[getField.Table()] = lookup
	case *expression.IsNull:
		return getIndexes(ctx, a, ia, expression.NewEquals(e.Child, expression.NewLiteral(nil, sql.Null)), tableAliases)
	case *expression.Not:
		r, err := getNegatedIndexes(ctx, a, ia, e, tableAliases)
		if err != nil {
			return nil, err
		}
		for table, indexLookup := range r {
			result[table] = indexLookup
		}
	case *expression.Between:
		if !isEvaluable(e.Val) && isEvaluable(e.Upper) && isEvaluable(e.Lower) {
			gf := extractGetField(e)
			if gf == nil {
				return nil, nil
			}
			idx := ia.IndexByExpression(ctx, ctx.GetCurrentDatabase(), gf.Table(), normalizeExpressions(ctx, tableAliases, e.Val)...)
			if idx != nil {
				upper, err := e.Upper.Eval(sql.NewEmptyContext(), nil)
				if err != nil {
					return nil, err
				}
				lower, err := e.Lower.Eval(sql.NewEmptyContext(), nil)
				if err != nil {
					return nil, err
				}
				lookup, err := betweenIndexLookup(
					idx,
					[]interface{}{upper},
					[]interface{}{lower},
				)
				if err != nil || lookup == nil {
					return nil, err
				}
				getField := extractGetField(e)
				if getField == nil {
					return result, nil
				}
				result[getField.Table()] = &indexLookup{
					exprs:   []sql.Expression{getField},
					indexes: []sql.Index{idx},
					lookup:  lookup,
				}
			}
		}
	case *expression.And:
		exprs := splitConjunction(e)
		multiColumnIndexes, err := getMultiColumnIndexes(ctx, exprs, a, ia, tableAliases)
		if err != nil {
			return nil, err
		}
		result := multiColumnIndexes
		for _, e := range exprs {
			if indexHasExpression(multiColumnIndexes, normalizeExpression(ctx, tableAliases, e)) {
				continue
			}
			indexes, err := getIndexes(ctx, a, ia, e, tableAliases)
			if err != nil {
				return nil, err
			}
			if !canMergeIndexLookups(result, indexes) {
				return nil, nil
			}
			result, err = indexesIntersection(result, indexes)
			if err != nil {
				return nil, err
			}
		}
		return result, nil
	}
	return result, nil
}