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  }