github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/row/noms_row.go (about)

     1  // Copyright 2019 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 row
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  
    21  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    22  	"github.com/dolthub/dolt/go/store/types"
    23  )
    24  
    25  type nomsRow struct {
    26  	key   TaggedValues
    27  	value TaggedValues
    28  	nbf   *types.NomsBinFormat
    29  }
    30  
    31  var _ Row = nomsRow{}
    32  
    33  func pkRowFromNoms(sch schema.Schema, nomsKey, nomsVal types.Tuple) (Row, error) {
    34  	keySl, err := nomsKey.AsSlice()
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	valSl, err := nomsVal.AsSlice()
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	allCols := sch.GetAllCols()
    45  
    46  	err = IterPkTuple(keySl, func(tag uint64, val types.Value) (stop bool, err error) {
    47  		// The IsKeyless check in FromNoms misses keyless index schemas, even though
    48  		// the output tuple is a keyless index that contains a KeylessRowIdTag.
    49  		// NomsRangeReader breaks without this.
    50  		// A longer term fix could separate record vs index parsing, each of
    51  		// which is different for keyless vs keyed tables.
    52  		if tag == schema.KeylessRowIdTag {
    53  			return false, nil
    54  		}
    55  		col, ok := allCols.GetByTag(tag)
    56  
    57  		if !ok {
    58  			return false, errors.New("Trying to set a value on an unknown tag is a bug for the key.  Validation should happen upstream. col:" + col.Name)
    59  		} else if !col.IsPartOfPK {
    60  			return false, errors.New("writing columns that are not part of the primary key to pk values. col:" + col.Name)
    61  		} else if !types.IsNull(val) && col.Kind != val.Kind() {
    62  			return false, errors.New("bug.  Setting a value to an incorrect kind. col: " + col.Name)
    63  		}
    64  
    65  		return false, nil
    66  	})
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	filteredVals := make(TaggedValues, len(valSl))
    72  	err = IterPkTuple(valSl, func(tag uint64, val types.Value) (stop bool, err error) {
    73  		col, ok := allCols.GetByTag(tag)
    74  		if !ok {
    75  			return false, nil
    76  		}
    77  
    78  		if col.IsPartOfPK {
    79  			return false, errors.New("writing columns that are part of the primary key to non-pk values. col:" + col.Name)
    80  		} else if !types.IsNull(val) {
    81  			// Column is GeometryKind and received PointKind, LineStringKind, or PolygonKind
    82  			if col.Kind == types.GeometryKind && types.IsGeometryKind(val.Kind()) {
    83  				filteredVals[tag] = val
    84  			} else if col.Kind == val.Kind() {
    85  				filteredVals[tag] = val
    86  			} else {
    87  				return false, errors.New("bug.  Setting a value to an incorrect kind. col:" + col.Name)
    88  			}
    89  
    90  		}
    91  
    92  		return false, nil
    93  	})
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	taggedKeyVals, err := TaggedValuesFromTupleValueSlice(keySl)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	return nomsRow{taggedKeyVals, filteredVals, nomsKey.Format()}, nil
   104  }
   105  
   106  func (nr nomsRow) IterSchema(sch schema.Schema, cb func(tag uint64, val types.Value) (stop bool, err error)) (bool, error) {
   107  	err := sch.GetAllCols().Iter(func(tag uint64, col schema.Column) (bool, error) {
   108  		value, _ := nr.GetColVal(tag)
   109  		return cb(tag, value)
   110  	})
   111  
   112  	return false, err
   113  }
   114  
   115  func (nr nomsRow) IterCols(cb func(tag uint64, val types.Value) (bool, error)) (bool, error) {
   116  	stopped, err := nr.key.Iter(cb)
   117  
   118  	if err != nil {
   119  		return false, err
   120  	}
   121  
   122  	if !stopped {
   123  		stopped, err = nr.value.Iter(cb)
   124  	}
   125  
   126  	if err != nil {
   127  		return false, err
   128  	}
   129  
   130  	return stopped, nil
   131  }
   132  
   133  func (nr nomsRow) GetColVal(tag uint64) (types.Value, bool) {
   134  	val, ok := nr.key.Get(tag)
   135  
   136  	if !ok {
   137  		val, ok = nr.value.Get(tag)
   138  	}
   139  
   140  	return val, ok
   141  }
   142  
   143  func (nr nomsRow) SetColVal(tag uint64, val types.Value, sch schema.Schema) (Row, error) {
   144  	rowKey := nr.key
   145  	rowVal := nr.value
   146  
   147  	cols := sch.GetAllCols()
   148  	col, ok := cols.GetByTag(tag)
   149  
   150  	if ok {
   151  		if col.IsPartOfPK {
   152  			rowKey = nr.key.Set(tag, val)
   153  		} else {
   154  			rowVal = nr.value.Set(tag, val)
   155  		}
   156  
   157  		return nomsRow{rowKey, rowVal, nr.nbf}, nil
   158  	}
   159  
   160  	panic("can't set a column whose tag isn't in the schema.  verify before calling this function.")
   161  }
   162  
   163  func (nr nomsRow) Format() *types.NomsBinFormat {
   164  	return nr.nbf
   165  }
   166  
   167  // TaggedValues implements the Row interface.
   168  func (nr nomsRow) TaggedValues() (TaggedValues, error) {
   169  	tv := make(TaggedValues)
   170  	for k, v := range nr.key {
   171  		tv[k] = v
   172  	}
   173  	for k, v := range nr.value {
   174  		tv[k] = v
   175  	}
   176  	return tv, nil
   177  }
   178  
   179  func pkRowFromTaggedValues(nbf *types.NomsBinFormat, sch schema.Schema, colVals TaggedValues) (Row, error) {
   180  	allCols := sch.GetAllCols()
   181  
   182  	keyVals := make(TaggedValues)
   183  	nonKeyVals := make(TaggedValues)
   184  
   185  	_, err := colVals.Iter(func(tag uint64, val types.Value) (stop bool, err error) {
   186  		col, ok := allCols.GetByTag(tag)
   187  
   188  		if !ok {
   189  			return false, errors.New("Trying to set a value on an unknown tag is a bug.  Validation should happen upstream.")
   190  		} else if col.IsPartOfPK {
   191  			keyVals[tag] = val
   192  		} else {
   193  			nonKeyVals[tag] = val
   194  		}
   195  		return false, nil
   196  	})
   197  
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	return fromTaggedVals(nbf, sch, keyVals, nonKeyVals)
   203  }
   204  
   205  // fromTaggedVals will take a schema, a map of tag to value for the key, and a map of tag to value for non key values,
   206  // and generates a row.  When a schema adds or removes columns from the non-key portion of the row, the schema will be
   207  // updated, but the rows will not be touched.  So the non-key portion of the row may contain values that are not in the
   208  // schema (The keys must match the schema though).
   209  func fromTaggedVals(nbf *types.NomsBinFormat, sch schema.Schema, keyVals, nonKeyVals TaggedValues) (Row, error) {
   210  	allCols := sch.GetAllCols()
   211  
   212  	_, err := keyVals.Iter(func(tag uint64, val types.Value) (stop bool, err error) {
   213  		col, ok := allCols.GetByTag(tag)
   214  
   215  		if !ok {
   216  			return false, errors.New("Trying to set a value on an unknown tag is a bug for the key.  Validation should happen upstream. col:" + col.Name)
   217  		} else if !col.IsPartOfPK {
   218  			return false, errors.New("writing columns that are not part of the primary key to pk values. col:" + col.Name)
   219  		} else if !types.IsNull(val) && col.Kind != val.Kind() {
   220  			if col.Kind == types.GeometryKind && types.IsGeometryKind(val.Kind()) {
   221  				return false, nil
   222  			}
   223  			return false, errors.New("bug.  Setting a value to an incorrect kind. col: " + col.Name)
   224  		}
   225  
   226  		return false, nil
   227  	})
   228  
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  
   233  	filteredVals := make(TaggedValues, len(nonKeyVals))
   234  	_, err = nonKeyVals.Iter(func(tag uint64, val types.Value) (stop bool, err error) {
   235  		col, ok := allCols.GetByTag(tag)
   236  		if !ok {
   237  			return false, nil
   238  		}
   239  
   240  		if col.IsPartOfPK {
   241  			return false, errors.New("writing columns that are part of the primary key to non-pk values. col:" + col.Name)
   242  		} else if !types.IsNull(val) && col.Kind != val.Kind() {
   243  			if col.Kind == types.GeometryKind && types.IsGeometryKind(val.Kind()) {
   244  				filteredVals[tag] = val
   245  				return false, nil
   246  			}
   247  			return false, errors.New("bug.  Setting a value to an incorrect kind. col:" + col.Name)
   248  		} else {
   249  			filteredVals[tag] = val
   250  		}
   251  
   252  		return false, nil
   253  	})
   254  
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	return nomsRow{keyVals, filteredVals, nbf}, nil
   260  }
   261  
   262  func (nr nomsRow) NomsMapKey(sch schema.Schema) types.LesserValuable {
   263  	return nr.key.NomsTupleForPKCols(nr.nbf, sch.GetPKCols())
   264  }
   265  
   266  func (nr nomsRow) NomsMapValue(sch schema.Schema) types.Valuable {
   267  	return nr.value.NomsTupleForNonPKCols(nr.nbf, sch.GetNonPKCols())
   268  }
   269  
   270  func (nr nomsRow) NomsMapKeyTuple(sch schema.Schema, tf *types.TupleFactory) (types.Tuple, error) {
   271  	tv := nr.key.NomsTupleForPKCols(nr.nbf, sch.GetPKCols())
   272  
   273  	if tf != nil {
   274  		return tf.Create(tv.vs...)
   275  	} else {
   276  		return types.NewTuple(tv.nbf, tv.vs...)
   277  	}
   278  }
   279  
   280  func (nr nomsRow) NomsMapValueTuple(sch schema.Schema, tf *types.TupleFactory) (types.Tuple, error) {
   281  	tv := nr.value.NomsTupleForNonPKCols(nr.nbf, sch.GetNonPKCols())
   282  
   283  	if tf != nil {
   284  		return tf.Create(tv.vs...)
   285  	} else {
   286  		return types.NewTuple(tv.nbf, tv.vs...)
   287  	}
   288  }
   289  
   290  // ReduceToIndexKeys creates a full key, partial key, and value tuple from the given row (first tuple being the full key). Please
   291  // refer to the note in the index editor for more information regarding partial keys. NomsRows map always
   292  // keys to an empty value tuple.
   293  func (nr nomsRow) ReduceToIndexKeys(idx schema.Index, tf *types.TupleFactory) (types.Tuple, types.Tuple, types.Tuple, error) {
   294  	vals := make([]types.Value, 0, len(idx.AllTags())*2)
   295  	for _, tag := range idx.AllTags() {
   296  		val, ok := nr.GetColVal(tag)
   297  		if !ok {
   298  			val = types.NullValue
   299  		}
   300  		vals = append(vals, types.Uint(tag), val)
   301  	}
   302  
   303  	var err error
   304  	var fullKey types.Tuple
   305  	var partialKey types.Tuple
   306  	if tf == nil {
   307  		fullKey, err = types.NewTuple(nr.Format(), vals...)
   308  		if err != nil {
   309  			return types.Tuple{}, types.Tuple{}, types.Tuple{}, err
   310  		}
   311  		partialKey, err = types.NewTuple(nr.Format(), vals[:idx.Count()*2]...)
   312  		if err != nil {
   313  			return types.Tuple{}, types.Tuple{}, types.Tuple{}, err
   314  		}
   315  	} else {
   316  		fullKey, err = tf.Create(vals...)
   317  		if err != nil {
   318  			return types.Tuple{}, types.Tuple{}, types.Tuple{}, err
   319  		}
   320  		partialKey, err = tf.Create(vals[:idx.Count()*2]...)
   321  		if err != nil {
   322  			return types.Tuple{}, types.Tuple{}, types.Tuple{}, err
   323  		}
   324  	}
   325  
   326  	return fullKey, partialKey, types.EmptyTuple(nr.Format()), nil
   327  }
   328  
   329  func IterPkTuple(tvs types.TupleValueSlice, cb func(tag uint64, val types.Value) (stop bool, err error)) error {
   330  	if len(tvs)%2 != 0 {
   331  		return fmt.Errorf("expected len(TupleValueSlice) to be even, got %d", len(tvs))
   332  	}
   333  
   334  	l := len(tvs)
   335  	for i := 0; i < l; i += 2 {
   336  		stop, err := cb(uint64(tvs[i].(types.Uint)), tvs[i+1])
   337  
   338  		if err != nil {
   339  			return err
   340  		}
   341  
   342  		if stop {
   343  			break
   344  		}
   345  	}
   346  
   347  	return nil
   348  }