github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/index/noms_index_iter.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 index 16 17 import ( 18 "context" 19 "io" 20 "sync" 21 22 "github.com/dolthub/go-mysql-server/sql" 23 24 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable" 25 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 26 "github.com/dolthub/dolt/go/libraries/doltcore/table/typed/noms" 27 "github.com/dolthub/dolt/go/libraries/utils/async" 28 "github.com/dolthub/dolt/go/store/types" 29 ) 30 31 const ( 32 ringBufferAllocSize = 1024 33 ) 34 35 var resultBufferPool = &sync.Pool{ 36 New: func() interface{} { 37 return async.NewRingBuffer(ringBufferAllocSize) 38 }, 39 } 40 41 type indexLookupRowIterAdapter struct { 42 idx DoltIndex 43 keyIter nomsKeyIter 44 tableRows types.Map 45 46 lookupTags map[uint64]int 47 conv *KVToSqlRowConverter 48 cancelF func() 49 50 read uint64 51 count uint64 52 53 resultBuf *async.RingBuffer 54 } 55 56 // NewIndexLookupRowIterAdapter returns a new indexLookupRowIterAdapter. 57 func NewIndexLookupRowIterAdapter(ctx *sql.Context, idx DoltIndex, durableState *durableIndexState, keyIter nomsKeyIter, columns []uint64) (*indexLookupRowIterAdapter, error) { 58 rows := durable.NomsMapFromIndex(durableState.Primary) 59 60 resBuf := resultBufferPool.Get().(*async.RingBuffer) 61 epoch := resBuf.Reset() 62 63 queueCtx, cancelF := context.WithCancel(ctx) 64 65 iter := &indexLookupRowIterAdapter{ 66 idx: idx, 67 keyIter: keyIter, 68 tableRows: rows, 69 conv: idx.sqlRowConverter(durableState, columns), 70 lookupTags: idx.lookupTags(durableState), 71 cancelF: cancelF, 72 resultBuf: resBuf, 73 } 74 75 go iter.queueRows(queueCtx, epoch) 76 return iter, nil 77 } 78 79 // Next returns the next row from the iterator. 80 func (i *indexLookupRowIterAdapter) Next(ctx *sql.Context) (sql.Row, error) { 81 for i.count == 0 || i.read < i.count { 82 item, err := i.resultBuf.Pop() 83 84 if err != nil { 85 return nil, err 86 } 87 88 res := item.(lookupResult) 89 90 i.read++ 91 if res.err != nil { 92 if res.err == io.EOF { 93 i.count = res.idx 94 continue 95 } 96 97 return nil, res.err 98 } 99 100 return res.r, res.err 101 } 102 103 return nil, io.EOF 104 } 105 106 func (i *indexLookupRowIterAdapter) Close(*sql.Context) error { 107 i.cancelF() 108 resultBufferPool.Put(i.resultBuf) 109 return nil 110 } 111 112 // queueRows reads each key from the key iterator and writes it to lookups.toLookupCh which manages a pool of worker 113 // routines which will process the requests in parallel. 114 func (i *indexLookupRowIterAdapter) queueRows(ctx context.Context, epoch int) { 115 for idx := uint64(1); ; idx++ { 116 indexKey, err := i.keyIter.ReadKey(ctx) 117 118 if err != nil { 119 i.resultBuf.Push(lookupResult{ 120 idx: idx, 121 r: nil, 122 err: err, 123 }, epoch) 124 125 return 126 } 127 128 lookup := toLookup{ 129 idx: idx, 130 t: indexKey, 131 tupleToRow: i.processKey, 132 resBuf: i.resultBuf, 133 epoch: epoch, 134 ctx: ctx, 135 } 136 137 select { 138 case lookups.toLookupCh <- lookup: 139 case <-ctx.Done(): 140 err := ctx.Err() 141 if err == nil { 142 err = io.EOF 143 } 144 i.resultBuf.Push(lookupResult{ 145 idx: idx, 146 r: nil, 147 err: err, 148 }, epoch) 149 150 return 151 } 152 } 153 } 154 155 func (i *indexLookupRowIterAdapter) indexKeyToTableKey(nbf *types.NomsBinFormat, indexKey types.Tuple) (types.Tuple, error) { 156 tplItr, err := indexKey.Iterator() 157 158 if err != nil { 159 return types.Tuple{}, err 160 } 161 162 resVals := make([]types.Value, len(i.lookupTags)*2) 163 for { 164 _, tag, err := tplItr.NextUint64() 165 166 if err != nil { 167 if err == io.EOF { 168 break 169 } 170 171 return types.Tuple{}, err 172 } 173 174 idx, inKey := i.lookupTags[tag] 175 176 if inKey { 177 _, valVal, err := tplItr.Next() 178 179 if err != nil { 180 return types.Tuple{}, err 181 } 182 183 resVals[idx*2] = types.Uint(tag) 184 resVals[idx*2+1] = valVal 185 } else { 186 err := tplItr.Skip() 187 188 if err != nil { 189 return types.Tuple{}, err 190 } 191 } 192 } 193 194 return types.NewTuple(nbf, resVals...) 195 } 196 197 // processKey is called within queueRows and processes each key, sending the resulting row to the row channel. 198 func (i *indexLookupRowIterAdapter) processKey(ctx context.Context, indexKey types.Tuple) (sql.Row, error) { 199 pkTupleVal, err := i.indexKeyToTableKey(i.idx.Format(), indexKey) 200 if err != nil { 201 return nil, err 202 } 203 204 fieldsVal, ok, err := i.tableRows.MaybeGetTuple(ctx, pkTupleVal) 205 if err != nil { 206 return nil, err 207 } 208 209 if !ok { 210 return nil, nil 211 } 212 213 sqlRow, err := i.conv.ConvertKVTuplesToSqlRow(pkTupleVal, fieldsVal) 214 if err != nil { 215 return nil, err 216 } 217 218 return sqlRow, nil 219 } 220 221 type CoveringIndexRowIterAdapter struct { 222 idx DoltIndex 223 rr *noms.NomsRangeReader 224 conv *KVToSqlRowConverter 225 ctx *sql.Context 226 pkCols *schema.ColCollection 227 nonPKCols *schema.ColCollection 228 nbf *types.NomsBinFormat 229 } 230 231 func NewCoveringIndexRowIterAdapter(ctx *sql.Context, idx DoltIndex, keyIter *noms.NomsRangeReader, resultCols []uint64) *CoveringIndexRowIterAdapter { 232 idxCols := idx.IndexSchema().GetPKCols() 233 tblPKCols := idx.Schema().GetPKCols() 234 sch := idx.Schema() 235 allCols := sch.GetAllCols().GetColumns() 236 cols := make([]schema.Column, 0) 237 for _, col := range allCols { 238 for _, tag := range resultCols { 239 if col.Tag == tag { 240 cols = append(cols, col) 241 } 242 } 243 244 } 245 tagToSqlColIdx := make(map[uint64]int) 246 isPrimaryKeyIdx := idx.ID() == "PRIMARY" 247 248 resultColSet := make(map[uint64]bool) 249 for _, k := range resultCols { 250 resultColSet[k] = true 251 } 252 for i, col := range cols { 253 _, partOfIdxKey := idxCols.GetByNameCaseInsensitive(col.Name) 254 // Either this is a primary key index or the key is a part of the index and this part of the result column set. 255 if (partOfIdxKey || isPrimaryKeyIdx) && (len(resultCols) == 0 || resultColSet[col.Tag]) { 256 tagToSqlColIdx[col.Tag] = i 257 } 258 } 259 260 for i, col := range cols { 261 _, partOfIndexKey := idxCols.GetByTag(col.Tag) 262 _, partOfTableKeys := tblPKCols.GetByTag(col.Tag) 263 if partOfIndexKey != partOfTableKeys { 264 cols[i], _ = schema.NewColumnWithTypeInfo(col.Name, col.Tag, col.TypeInfo, partOfIndexKey, col.Default, col.AutoIncrement, col.Comment, col.Constraints...) 265 } 266 } 267 268 return &CoveringIndexRowIterAdapter{ 269 idx: idx, 270 rr: keyIter, 271 conv: NewKVToSqlRowConverter(idx.Format(), tagToSqlColIdx, cols, len(cols)), 272 ctx: ctx, 273 pkCols: sch.GetPKCols(), 274 nonPKCols: sch.GetNonPKCols(), 275 nbf: idx.Format(), 276 } 277 } 278 279 // Next returns the next row from the iterator. 280 func (ci *CoveringIndexRowIterAdapter) Next(ctx *sql.Context) (sql.Row, error) { 281 key, value, err := ci.rr.ReadKV(ctx) 282 283 if err != nil { 284 return nil, err 285 } 286 287 return ci.conv.ConvertKVTuplesToSqlRow(key, value) 288 } 289 290 func (ci *CoveringIndexRowIterAdapter) Close(*sql.Context) error { 291 return nil 292 }