func getComparisonIndexLookup(
	ctx *sql.Context,
	a *Analyzer,
	ia *indexAnalyzer,
	e expression.Comparer,
	tableAliases TableAliases,
) (*indexLookup, error) {
	left, right := e.Left(), e.Right()
	if !isEvaluable(right) {
		left, right, e = swapTermsOfExpression(e)
	}
	if !isEvaluable(left) && isEvaluable(right) {
		gf := extractGetField(left)
		if gf == nil {
			return nil, nil
		}
		idx := ia.IndexByExpression(ctx, ctx.GetCurrentDatabase(), gf.Table(), normalizeExpressions(ctx, tableAliases, left)...)
		if idx != nil {
			value, err := right.Eval(sql.NewEmptyContext(), nil)
			if err != nil {
				return nil, err
			}
			lookup, err := comparisonIndexLookup(e, idx, value)
			if err != nil || lookup == nil {
				return nil, err
			}
			return &indexLookup{
				exprs:   []sql.Expression{left},
				lookup:  lookup,
				indexes: []sql.Index{idx},
			}, nil
		}
	}
	return nil, nil
}
getComparisonIndexLookup returns the index and index lookup for the given
comparison if any index can be found.
It works for the following comparisons: eq, lt, gt, gte and lte.
TODO: add support for BETWEEN
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
}
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
}
func betweenIndexLookup(index sql.Index, upper, lower []interface{}) (sql.IndexLookup, error) {
	ai, isAscend := index.(sql.AscendIndex)
	di, isDescend := index.(sql.DescendIndex)
	if isAscend && isDescend {
		ascendLookup, err := ai.AscendRange(lower, upper)
		if err != nil {
			return nil, err
		}
		descendLookup, err := di.DescendRange(upper, lower)
		if err != nil {
			return nil, err
		}
		m, ok := ascendLookup.(sql.MergeableIndexLookup)
		if ok && m.IsMergeable(descendLookup) {
			return ascendLookup.(sql.MergeableIndexLookup).Union(descendLookup)
		}
	}
	return nil, nil
}