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  }