github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/index_row_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 sqle 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/schema" 25 "github.com/dolthub/dolt/go/libraries/utils/async" 26 "github.com/dolthub/dolt/go/libraries/utils/set" 27 "github.com/dolthub/dolt/go/store/types" 28 ) 29 30 const ( 31 ringBufferAllocSize = 1024 32 ) 33 34 var resultBufferPool = &sync.Pool{ 35 New: func() interface{} { 36 return async.NewRingBuffer(ringBufferAllocSize) 37 }, 38 } 39 40 type indexLookupRowIterAdapter struct { 41 idx DoltIndex 42 keyIter nomsKeyIter 43 pkTags map[uint64]int 44 conv *KVToSqlRowConverter 45 ctx *sql.Context 46 cancelF func() 47 48 read uint64 49 count uint64 50 51 resultBuf *async.RingBuffer 52 } 53 54 // NewIndexLookupRowIterAdapter returns a new indexLookupRowIterAdapter. 55 func NewIndexLookupRowIterAdapter(ctx *sql.Context, idx DoltIndex, keyIter nomsKeyIter) *indexLookupRowIterAdapter { 56 pkTags := make(map[uint64]int) 57 for i, tag := range idx.Schema().GetPKCols().Tags { 58 pkTags[tag] = i 59 } 60 61 cols := idx.Schema().GetAllCols().GetColumns() 62 conv := NewKVToSqlRowConverterForCols(idx.IndexRowData().Format(), cols) 63 resBuf := resultBufferPool.Get().(*async.RingBuffer) 64 epoch := resBuf.Reset() 65 66 queueCtx, cancelF := context.WithCancel(ctx) 67 68 iter := &indexLookupRowIterAdapter{ 69 idx: idx, 70 keyIter: keyIter, 71 conv: conv, 72 pkTags: pkTags, 73 ctx: ctx, 74 cancelF: cancelF, 75 resultBuf: resBuf, 76 } 77 78 go iter.queueRows(queueCtx, epoch) 79 return iter 80 } 81 82 // Next returns the next row from the iterator. 83 func (i *indexLookupRowIterAdapter) Next() (sql.Row, error) { 84 for i.count == 0 || i.read < i.count { 85 item, err := i.resultBuf.Pop() 86 87 if err != nil { 88 return nil, err 89 } 90 91 res := item.(lookupResult) 92 93 i.read++ 94 if res.err != nil { 95 if res.err == io.EOF { 96 i.count = res.idx 97 continue 98 } 99 100 return nil, res.err 101 } 102 103 return res.r, res.err 104 } 105 106 return nil, io.EOF 107 } 108 109 func (i *indexLookupRowIterAdapter) Close(*sql.Context) error { 110 i.cancelF() 111 resultBufferPool.Put(i.resultBuf) 112 return nil 113 } 114 115 // queueRows reads each key from the key iterator and writes it to lookups.toLookupCh which manages a pool of worker 116 // routines which will process the requests in parallel. 117 func (i *indexLookupRowIterAdapter) queueRows(ctx context.Context, epoch int) { 118 for idx := uint64(1); ; idx++ { 119 indexKey, err := i.keyIter.ReadKey(i.ctx) 120 121 if err != nil { 122 i.resultBuf.Push(lookupResult{ 123 idx: idx, 124 r: nil, 125 err: err, 126 }, epoch) 127 128 return 129 } 130 131 lookup := toLookup{ 132 idx: idx, 133 t: indexKey, 134 tupleToRow: i.processKey, 135 resBuf: i.resultBuf, 136 epoch: epoch, 137 } 138 139 select { 140 case lookups.toLookupCh <- lookup: 141 case <-ctx.Done(): 142 i.resultBuf.Push(lookupResult{ 143 idx: idx, 144 r: nil, 145 err: ctx.Err(), 146 }, epoch) 147 148 return 149 } 150 } 151 } 152 153 func (i *indexLookupRowIterAdapter) indexKeyToTableKey(nbf *types.NomsBinFormat, indexKey types.Tuple) (types.Tuple, error) { 154 tplItr, err := indexKey.Iterator() 155 156 if err != nil { 157 return types.Tuple{}, err 158 } 159 160 resVals := make([]types.Value, len(i.pkTags)*2) 161 for { 162 _, tag, err := tplItr.NextUint64() 163 164 if err != nil { 165 if err == io.EOF { 166 break 167 } 168 169 return types.Tuple{}, err 170 } 171 172 idx, inPK := i.pkTags[tag] 173 174 if inPK { 175 _, valVal, err := tplItr.Next() 176 177 if err != nil { 178 return types.Tuple{}, err 179 } 180 181 resVals[idx*2] = types.Uint(tag) 182 resVals[idx*2+1] = valVal 183 } else { 184 err := tplItr.Skip() 185 186 if err != nil { 187 return types.Tuple{}, err 188 } 189 } 190 } 191 192 return types.NewTuple(nbf, resVals...) 193 } 194 195 // processKey is called within queueRows and processes each key, sending the resulting row to the row channel. 196 func (i *indexLookupRowIterAdapter) processKey(indexKey types.Tuple) (sql.Row, error) { 197 tableData := i.idx.TableData() 198 pkTupleVal, err := i.indexKeyToTableKey(tableData.Format(), indexKey) 199 200 if err != nil { 201 return nil, err 202 } 203 204 fieldsVal, ok, err := tableData.MaybeGetTuple(i.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 keyIter nomsKeyIter 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 nomsKeyIter, resultCols []string) *coveringIndexRowIterAdapter { 232 idxCols := idx.IndexSchema().GetPKCols() 233 tblPKCols := idx.Schema().GetPKCols() 234 sch := idx.Schema() 235 cols := sch.GetAllCols().GetColumns() 236 tagToSqlColIdx := make(map[uint64]int) 237 238 resultColSet := set.NewCaseInsensitiveStrSet(resultCols) 239 for i, col := range cols { 240 _, partOfIdxKey := idxCols.GetByNameCaseInsensitive(col.Name) 241 if partOfIdxKey && (len(resultCols) == 0 || resultColSet.Contains(col.Name)) { 242 tagToSqlColIdx[col.Tag] = i 243 } 244 } 245 246 for i, col := range cols { 247 _, partOfIndexKey := idxCols.GetByTag(col.Tag) 248 _, partOfTableKeys := tblPKCols.GetByTag(col.Tag) 249 if partOfIndexKey != partOfTableKeys { 250 cols[i], _ = schema.NewColumnWithTypeInfo(col.Name, col.Tag, col.TypeInfo, partOfIndexKey, col.Default, col.AutoIncrement, col.Comment, col.Constraints...) 251 } 252 } 253 254 return &coveringIndexRowIterAdapter{ 255 idx: idx, 256 keyIter: keyIter, 257 conv: NewKVToSqlRowConverter(idx.IndexRowData().Format(), tagToSqlColIdx, cols, len(cols)), 258 ctx: ctx, 259 pkCols: sch.GetPKCols(), 260 nonPKCols: sch.GetNonPKCols(), 261 nbf: idx.TableData().Format(), 262 } 263 } 264 265 // Next returns the next row from the iterator. 266 func (ci *coveringIndexRowIterAdapter) Next() (sql.Row, error) { 267 key, err := ci.keyIter.ReadKey(ci.ctx) 268 269 if err != nil { 270 return nil, err 271 } 272 273 return ci.conv.ConvertKVTuplesToSqlRow(key, types.Tuple{}) 274 } 275 276 func (ci *coveringIndexRowIterAdapter) Close(*sql.Context) error { 277 return nil 278 }