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 }