github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/index_lookup.go (about)

     1  // Copyright 2020 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package sqle
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  
    21  	"github.com/dolthub/go-mysql-server/sql"
    22  
    23  	"github.com/dolthub/dolt/go/libraries/doltcore/row"
    24  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/lookup"
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/table/typed/noms"
    26  	"github.com/dolthub/dolt/go/store/types"
    27  )
    28  
    29  type IndexLookupKeyIterator interface {
    30  	// NextKey returns the next key if it exists, and io.EOF if it does not.
    31  	NextKey(ctx *sql.Context) (row.TaggedValues, error)
    32  }
    33  
    34  type doltIndexLookup struct {
    35  	idx    DoltIndex
    36  	ranges []lookup.Range // The collection of ranges that represent this lookup.
    37  }
    38  
    39  var _ sql.MergeableIndexLookup = (*doltIndexLookup)(nil)
    40  
    41  func (il *doltIndexLookup) String() string {
    42  	// TODO: this could be expanded with additional info (like the expression used to create the index lookup)
    43  	return fmt.Sprintf("doltIndexLookup:%s", il.idx.ID())
    44  }
    45  
    46  func (il *doltIndexLookup) IndexRowData() types.Map {
    47  	return il.idx.IndexRowData()
    48  }
    49  
    50  // IsMergeable implements sql.MergeableIndexLookup
    51  func (il *doltIndexLookup) IsMergeable(indexLookup sql.IndexLookup) bool {
    52  	otherIl, ok := indexLookup.(*doltIndexLookup)
    53  	if !ok {
    54  		return false
    55  	}
    56  
    57  	return il.idx.Equals(otherIl.idx)
    58  }
    59  
    60  // Intersection implements sql.MergeableIndexLookup
    61  func (il *doltIndexLookup) Intersection(indexLookups ...sql.IndexLookup) (sql.IndexLookup, error) {
    62  	rangeCombinations := make([][]lookup.Range, len(il.ranges))
    63  	for i, ilRange := range il.ranges {
    64  		rangeCombinations[i] = []lookup.Range{ilRange}
    65  	}
    66  	for _, indexLookup := range indexLookups {
    67  		otherIl, ok := indexLookup.(*doltIndexLookup)
    68  		if !ok {
    69  			return nil, fmt.Errorf("failed to intersect sql.IndexLookup with type '%T'", indexLookup)
    70  		}
    71  		var newRangeCombination [][]lookup.Range
    72  		for _, rangeCombination := range rangeCombinations {
    73  			for _, ilRange := range otherIl.ranges {
    74  				rc := make([]lookup.Range, len(rangeCombination)+1)
    75  				copy(rc, rangeCombination)
    76  				rc[len(rangeCombination)] = ilRange
    77  				newRangeCombination = append(newRangeCombination, rc)
    78  			}
    79  		}
    80  		rangeCombinations = newRangeCombination
    81  	}
    82  	var newRanges []lookup.Range
    83  	var err error
    84  	var ok bool
    85  	for _, rangeCombination := range rangeCombinations {
    86  		intersectedRange := lookup.AllRange()
    87  		for _, rangeToIntersect := range rangeCombination {
    88  			intersectedRange, ok, err = intersectedRange.TryIntersect(rangeToIntersect)
    89  			if err != nil {
    90  				return nil, err
    91  			}
    92  			if !ok {
    93  				break
    94  			}
    95  		}
    96  		if !intersectedRange.IsEmpty() {
    97  			newRanges = append(newRanges, intersectedRange)
    98  		}
    99  	}
   100  	newRanges, err = lookup.SimplifyRanges(newRanges)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	return &doltIndexLookup{
   105  		idx:    il.idx,
   106  		ranges: newRanges,
   107  	}, nil
   108  }
   109  
   110  // Union implements sql.MergeableIndexLookup
   111  func (il *doltIndexLookup) Union(indexLookups ...sql.IndexLookup) (sql.IndexLookup, error) {
   112  	var ranges []lookup.Range
   113  	var err error
   114  	if len(il.ranges) == 0 {
   115  		ranges = []lookup.Range{lookup.EmptyRange()}
   116  	} else {
   117  		ranges = make([]lookup.Range, len(il.ranges))
   118  		copy(ranges, il.ranges)
   119  	}
   120  	for _, indexLookup := range indexLookups {
   121  		otherIl, ok := indexLookup.(*doltIndexLookup)
   122  		if !ok {
   123  			return nil, fmt.Errorf("failed to union sql.IndexLookup with type '%T'", indexLookup)
   124  		}
   125  		ranges = append(ranges, otherIl.ranges...)
   126  	}
   127  	ranges, err = lookup.SimplifyRanges(ranges)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	return &doltIndexLookup{
   132  		idx:    il.idx,
   133  		ranges: ranges,
   134  	}, nil
   135  }
   136  
   137  // RowIter returns a row iterator for this index lookup. The iterator will return the single matching row for the index.
   138  func (il *doltIndexLookup) RowIter(ctx *sql.Context, rowData types.Map, columns []string) (sql.RowIter, error) {
   139  	return il.RowIterForRanges(ctx, rowData, il.ranges, columns)
   140  }
   141  
   142  func (il *doltIndexLookup) indexCoversCols(cols []string) bool {
   143  	if cols == nil {
   144  		return false
   145  	}
   146  
   147  	idxCols := il.idx.IndexSchema().GetPKCols()
   148  	covers := true
   149  	for _, colName := range cols {
   150  		if _, ok := idxCols.GetByNameCaseInsensitive(colName); !ok {
   151  			covers = false
   152  			break
   153  		}
   154  	}
   155  
   156  	return covers
   157  }
   158  
   159  func (il *doltIndexLookup) RowIterForRanges(ctx *sql.Context, rowData types.Map, ranges []lookup.Range, columns []string) (sql.RowIter, error) {
   160  	readRanges := make([]*noms.ReadRange, len(ranges))
   161  	for i, lookupRange := range ranges {
   162  		readRanges[i] = lookupRange.ToReadRange()
   163  	}
   164  
   165  	nrr := noms.NewNomsRangeReader(il.idx.IndexSchema(), rowData, readRanges)
   166  
   167  	covers := il.indexCoversCols(columns)
   168  	if covers {
   169  		return NewCoveringIndexRowIterAdapter(ctx, il.idx, nrr, columns), nil
   170  	} else {
   171  		return NewIndexLookupRowIterAdapter(ctx, il.idx, nrr), nil
   172  	}
   173  }
   174  
   175  type nomsKeyIter interface {
   176  	ReadKey(ctx context.Context) (types.Tuple, error)
   177  }