github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/index/dolt_map_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  	"errors"
    20  	"io"
    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/store/types"
    26  )
    27  
    28  func maxU64(x, y uint64) uint64 {
    29  	if x > y {
    30  		return x
    31  	}
    32  
    33  	return y
    34  }
    35  
    36  // KVToSqlRowConverter takes noms types.Value key value pairs and converts them directly to a sql.Row.  It
    37  // can be configured to only process a portion of the columns and map columns to desired output columns.
    38  type KVToSqlRowConverter struct {
    39  	nbf            *types.NomsBinFormat
    40  	cols           []schema.Column
    41  	tagToSqlColIdx map[uint64]int
    42  	// rowSize is the number of columns in the output row.  This may be bigger than the number of columns being converted,
    43  	// but not less.  When rowSize is bigger than the number of columns being processed that means that some of the columns
    44  	// in the output row will be filled with nils
    45  	rowSize     int
    46  	valsFromKey int
    47  	valsFromVal int
    48  	maxValTag   uint64
    49  }
    50  
    51  func NewKVToSqlRowConverter(nbf *types.NomsBinFormat, tagToSqlColIdx map[uint64]int, cols []schema.Column, rowSize int) *KVToSqlRowConverter {
    52  	valsFromKey, valsFromVal, maxValTag := getValLocations(tagToSqlColIdx, cols)
    53  
    54  	return &KVToSqlRowConverter{
    55  		nbf:            nbf,
    56  		cols:           cols,
    57  		tagToSqlColIdx: tagToSqlColIdx,
    58  		rowSize:        rowSize,
    59  		valsFromKey:    valsFromKey,
    60  		valsFromVal:    valsFromVal,
    61  		maxValTag:      maxValTag,
    62  	}
    63  }
    64  
    65  // get counts of where the values we want converted come from so we can skip entire tuples at times.
    66  func getValLocations(tagToSqlColIdx map[uint64]int, cols []schema.Column) (int, int, uint64) {
    67  	var fromKey int
    68  	var fromVal int
    69  	var maxValTag uint64
    70  	for _, col := range cols {
    71  		if _, ok := tagToSqlColIdx[col.Tag]; ok {
    72  			if col.IsPartOfPK {
    73  				fromKey++
    74  			} else {
    75  				fromVal++
    76  				maxValTag = maxU64(maxValTag, col.Tag)
    77  			}
    78  		}
    79  	}
    80  
    81  	return fromKey, fromVal, maxValTag
    82  }
    83  
    84  // NewKVToSqlRowConverterForCols returns a KVToSqlConverter instance based on the list of columns passed in
    85  func NewKVToSqlRowConverterForCols(nbf *types.NomsBinFormat, sch schema.Schema, columns []uint64) *KVToSqlRowConverter {
    86  	allCols := sch.GetAllCols().GetColumns()
    87  	tagToSqlColIdx := make(map[uint64]int)
    88  	var outCols []schema.Column
    89  	if len(columns) > 0 {
    90  		outCols = make([]schema.Column, len(columns))
    91  		for i, tag := range columns {
    92  			schIdx := sch.GetAllCols().TagToIdx[tag]
    93  			outCols[i] = allCols[schIdx]
    94  			tagToSqlColIdx[tag] = i
    95  		}
    96  	} else {
    97  		outCols = allCols
    98  		for i, col := range allCols {
    99  			tagToSqlColIdx[col.Tag] = i
   100  		}
   101  	}
   102  
   103  	return NewKVToSqlRowConverter(nbf, tagToSqlColIdx, outCols, len(outCols))
   104  }
   105  
   106  // ConvertKVToSqlRow returns a sql.Row generated from the key and value provided.
   107  func (conv *KVToSqlRowConverter) ConvertKVToSqlRow(k, v types.Value) (sql.Row, error) {
   108  	keyTup, ok := k.(types.Tuple)
   109  
   110  	if !ok {
   111  		return nil, errors.New("invalid key is not a tuple")
   112  	}
   113  
   114  	var valTup types.Tuple
   115  	if !types.IsNull(v) {
   116  		valTup, ok = v.(types.Tuple)
   117  
   118  		if !ok {
   119  			return nil, errors.New("invalid value is not a tuple")
   120  		}
   121  	} else {
   122  		valTup = types.EmptyTuple(conv.nbf)
   123  	}
   124  
   125  	return conv.ConvertKVTuplesToSqlRow(keyTup, valTup)
   126  }
   127  
   128  // ConvertKVTuplesToSqlRow returns a sql.Row generated from the key and value provided.
   129  func (conv *KVToSqlRowConverter) ConvertKVTuplesToSqlRow(k, v types.Tuple) (sql.Row, error) {
   130  	tupItr := types.TupleItrPool.Get().(*types.TupleIterator)
   131  	defer types.TupleItrPool.Put(tupItr)
   132  
   133  	cols := make([]interface{}, conv.rowSize)
   134  	if conv.valsFromKey > 0 {
   135  		// keys are not in sorted order so cannot use max tag to early exit
   136  		err := conv.processTuple(cols, conv.valsFromKey, 0xFFFFFFFFFFFFFFFF, k, tupItr)
   137  
   138  		if err != nil {
   139  			return nil, err
   140  		}
   141  	}
   142  
   143  	if conv.valsFromVal > 0 {
   144  		err := conv.processTuple(cols, conv.valsFromVal, conv.maxValTag, v, tupItr)
   145  
   146  		if err != nil {
   147  			return nil, err
   148  		}
   149  	}
   150  
   151  	return cols, nil
   152  }
   153  
   154  func (conv *KVToSqlRowConverter) processTuple(cols []interface{}, valsToFill int, maxTag uint64, tup types.Tuple, tupItr *types.TupleIterator) error {
   155  	err := tupItr.InitForTuple(tup)
   156  
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	nbf := tup.Format()
   162  	primReader, numPrimitives := tupItr.CodecReader()
   163  
   164  	filled := 0
   165  	for pos := uint64(0); pos+1 < numPrimitives; pos += 2 {
   166  		if filled >= valsToFill {
   167  			break
   168  		}
   169  
   170  		tagKind := primReader.ReadKind()
   171  
   172  		if tagKind != types.UintKind {
   173  			return errors.New("Encountered unexpected kind while attempting to read tag")
   174  		}
   175  
   176  		tag64 := primReader.ReadUint()
   177  
   178  		if tag64 > maxTag && tag64 != schema.KeylessRowCardinalityTag && tag64 != schema.KeylessRowIdTag {
   179  			break
   180  		}
   181  
   182  		if sqlColIdx, ok := conv.tagToSqlColIdx[tag64]; !ok {
   183  			err = primReader.SkipValue(nbf)
   184  
   185  			if err != nil {
   186  				return err
   187  			}
   188  		} else {
   189  			cols[sqlColIdx], err = conv.cols[sqlColIdx].TypeInfo.ReadFrom(nbf, primReader)
   190  
   191  			if err != nil {
   192  				return err
   193  			}
   194  
   195  			filled++
   196  		}
   197  	}
   198  
   199  	return nil
   200  }
   201  
   202  // KVGetFunc defines a function that returns a Key Value pair
   203  type KVGetFunc func(ctx context.Context) (types.Tuple, types.Tuple, error)
   204  
   205  func GetGetFuncForMapIter(nbf *types.NomsBinFormat, mapItr types.MapIterator) func(ctx context.Context) (types.Tuple, types.Tuple, error) {
   206  	return func(ctx context.Context) (types.Tuple, types.Tuple, error) {
   207  		k, v, err := mapItr.Next(ctx)
   208  
   209  		if err != nil {
   210  			return types.Tuple{}, types.Tuple{}, err
   211  		} else if k == nil {
   212  			return types.Tuple{}, types.Tuple{}, io.EOF
   213  		}
   214  
   215  		valTup, ok := v.(types.Tuple)
   216  		if !ok {
   217  			valTup = types.EmptyTuple(nbf)
   218  		}
   219  
   220  		return k.(types.Tuple), valTup, nil
   221  	}
   222  }
   223  
   224  // DoltMapIter uses a types.MapIterator to iterate over a types.Map and returns sql.Row instances that it reads and
   225  // converts
   226  type DoltMapIter struct {
   227  	kvGet         KVGetFunc
   228  	closeKVGetter func() error
   229  	conv          *KVToSqlRowConverter
   230  }
   231  
   232  // NewDoltMapIter returns a new DoltMapIter
   233  func NewDoltMapIter(keyValGet KVGetFunc, closeKVGetter func() error, conv *KVToSqlRowConverter) *DoltMapIter {
   234  	return &DoltMapIter{
   235  		kvGet:         keyValGet,
   236  		closeKVGetter: closeKVGetter,
   237  		conv:          conv,
   238  	}
   239  }
   240  
   241  // Next returns the next sql.Row until all rows are returned at which point (nil, io.EOF) is returned.
   242  func (dmi *DoltMapIter) Next(ctx *sql.Context) (sql.Row, error) {
   243  	k, v, err := dmi.kvGet(ctx)
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  
   248  	return dmi.conv.ConvertKVTuplesToSqlRow(k, v)
   249  }
   250  
   251  func (dmi *DoltMapIter) Close(*sql.Context) error {
   252  	if dmi.closeKVGetter != nil {
   253  		return dmi.closeKVGetter()
   254  	}
   255  
   256  	return nil
   257  }