github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/row/noms_row_test.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  	"context"
    19  	"fmt"
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
    27  	"github.com/dolthub/dolt/go/store/types"
    28  )
    29  
    30  const (
    31  	lnColName       = "last"
    32  	fnColName       = "first"
    33  	addrColName     = "address"
    34  	ageColName      = "age"
    35  	titleColName    = "title"
    36  	reservedColName = "reserved"
    37  	indexName       = "idx_age"
    38  	lnColTag        = 1
    39  	fnColTag        = 0
    40  	addrColTag      = 6
    41  	ageColTag       = 4
    42  	titleColTag     = 40
    43  	reservedColTag  = 50
    44  	unusedTag       = 100
    45  )
    46  
    47  var lnVal = types.String("astley")
    48  var fnVal = types.String("rick")
    49  var addrVal = types.String("123 Fake St")
    50  var ageVal = types.Uint(53)
    51  var titleVal = types.NullValue
    52  
    53  var testKeyCols = []schema.Column{
    54  	{Name: lnColName, Tag: lnColTag, Kind: types.StringKind, IsPartOfPK: true, TypeInfo: typeinfo.StringDefaultType, Constraints: []schema.ColConstraint{schema.NotNullConstraint{}}},
    55  	{Name: fnColName, Tag: fnColTag, Kind: types.StringKind, IsPartOfPK: true, TypeInfo: typeinfo.StringDefaultType, Constraints: []schema.ColConstraint{schema.NotNullConstraint{}}},
    56  }
    57  var testCols = []schema.Column{
    58  	{Name: addrColName, Tag: addrColTag, Kind: types.StringKind, IsPartOfPK: false, TypeInfo: typeinfo.StringDefaultType, Constraints: nil},
    59  	{Name: ageColName, Tag: ageColTag, Kind: types.UintKind, IsPartOfPK: false, TypeInfo: typeinfo.Uint64Type, Constraints: nil},
    60  	{Name: titleColName, Tag: titleColTag, Kind: types.StringKind, IsPartOfPK: false, TypeInfo: typeinfo.StringDefaultType, Constraints: nil},
    61  	{Name: reservedColName, Tag: reservedColTag, Kind: types.StringKind, IsPartOfPK: false, TypeInfo: typeinfo.StringDefaultType, Constraints: nil},
    62  }
    63  var testKeyColColl = schema.NewColCollection(testKeyCols...)
    64  var testNonKeyColColl = schema.NewColCollection(testCols...)
    65  var sch, _ = schema.SchemaFromPKAndNonPKCols(testKeyColColl, testNonKeyColColl)
    66  var index schema.Index
    67  
    68  func init() {
    69  	index, _ = sch.Indexes().AddIndexByColTags(indexName, []uint64{ageColTag}, schema.IndexProperties{IsUnique: false, Comment: ""})
    70  }
    71  
    72  func newTestRow() (Row, error) {
    73  	vals := TaggedValues{
    74  		fnColTag:    fnVal,
    75  		lnColTag:    lnVal,
    76  		addrColTag:  addrVal,
    77  		ageColTag:   ageVal,
    78  		titleColTag: titleVal,
    79  	}
    80  
    81  	return New(types.Format_Default, sch, vals)
    82  }
    83  
    84  func TestItrRowCols(t *testing.T) {
    85  	r, err := newTestRow()
    86  	require.NoError(t, err)
    87  
    88  	itrVals := make(TaggedValues)
    89  	_, err = r.IterCols(func(tag uint64, val types.Value) (stop bool, err error) {
    90  		itrVals[tag] = val
    91  		return false, nil
    92  	})
    93  	require.NoError(t, err)
    94  
    95  	assert.Equal(t, TaggedValues{
    96  		lnColTag:    lnVal,
    97  		fnColTag:    fnVal,
    98  		ageColTag:   ageVal,
    99  		addrColTag:  addrVal,
   100  		titleColTag: titleVal,
   101  	}, itrVals)
   102  }
   103  
   104  func TestFromNoms(t *testing.T) {
   105  	// New() will faithfully return null values in the row, but such columns won't ever be set when loaded from Noms.
   106  	// So we use a row here with no null values set to avoid this inconsistency.
   107  	expectedRow, err := New(types.Format_Default, sch, TaggedValues{
   108  		fnColTag:   fnVal,
   109  		lnColTag:   lnVal,
   110  		addrColTag: addrVal,
   111  		ageColTag:  ageVal,
   112  	})
   113  	require.NoError(t, err)
   114  
   115  	t.Run("all values specified", func(t *testing.T) {
   116  		keys, err := types.NewTuple(types.Format_Default,
   117  			types.Uint(fnColTag), fnVal,
   118  			types.Uint(lnColTag), lnVal,
   119  		)
   120  		require.NoError(t, err)
   121  
   122  		vals, err := types.NewTuple(types.Format_Default,
   123  			types.Uint(addrColTag), addrVal,
   124  			types.Uint(ageColTag), ageVal,
   125  			types.Uint(titleColTag), titleVal,
   126  		)
   127  
   128  		require.NoError(t, err)
   129  		r, err := FromNoms(sch, keys, vals)
   130  
   131  		require.NoError(t, err)
   132  		assert.Equal(t, expectedRow, r)
   133  	})
   134  
   135  	t.Run("only key", func(t *testing.T) {
   136  		keys, err := types.NewTuple(types.Format_Default,
   137  			types.Uint(fnColTag), fnVal,
   138  			types.Uint(lnColTag), lnVal,
   139  		)
   140  		require.NoError(t, err)
   141  
   142  		vals, err := types.NewTuple(types.Format_Default)
   143  		require.NoError(t, err)
   144  
   145  		expectedRow, err := New(types.Format_Default, sch, TaggedValues{
   146  			fnColTag: fnVal,
   147  			lnColTag: lnVal,
   148  		})
   149  		require.NoError(t, err)
   150  		r, err := FromNoms(sch, keys, vals)
   151  		require.NoError(t, err)
   152  		assert.Equal(t, expectedRow, r)
   153  	})
   154  
   155  	t.Run("additional tag not in schema is silently dropped", func(t *testing.T) {
   156  		keys, err := types.NewTuple(types.Format_Default,
   157  			types.Uint(fnColTag), fnVal,
   158  			types.Uint(lnColTag), lnVal,
   159  		)
   160  
   161  		require.NoError(t, err)
   162  
   163  		vals, err := types.NewTuple(types.Format_Default,
   164  			types.Uint(addrColTag), addrVal,
   165  			types.Uint(ageColTag), ageVal,
   166  			types.Uint(titleColTag), titleVal,
   167  			types.Uint(unusedTag), fnVal,
   168  		)
   169  
   170  		require.NoError(t, err)
   171  
   172  		r, err := FromNoms(sch, keys, vals)
   173  		require.NoError(t, err)
   174  		assert.Equal(t, expectedRow, r)
   175  	})
   176  
   177  	t.Run("bad type", func(t *testing.T) {
   178  		keys, err := types.NewTuple(types.Format_Default,
   179  			types.Uint(fnColTag), fnVal,
   180  			types.Uint(lnColTag), lnVal,
   181  		)
   182  		require.NoError(t, err)
   183  		vals, err := types.NewTuple(types.Format_Default,
   184  			types.Uint(addrColTag), addrVal,
   185  			types.Uint(ageColTag), fnVal,
   186  		)
   187  		require.NoError(t, err)
   188  
   189  		_, err = FromNoms(sch, keys, vals)
   190  		assert.Error(t, err)
   191  	})
   192  
   193  	t.Run("key col set in vals", func(t *testing.T) {
   194  		keys, err := types.NewTuple(types.Format_Default,
   195  			types.Uint(fnColTag), fnVal,
   196  			types.Uint(lnColTag), lnVal,
   197  		)
   198  		require.NoError(t, err)
   199  		vals, err := types.NewTuple(types.Format_Default,
   200  			types.Uint(addrColTag), addrVal,
   201  			types.Uint(fnColTag), fnVal,
   202  		)
   203  		require.NoError(t, err)
   204  
   205  		_, err = FromNoms(sch, keys, vals)
   206  		assert.Error(t, err)
   207  	})
   208  
   209  	t.Run("unknown tag in key", func(t *testing.T) {
   210  		keys, err := types.NewTuple(types.Format_Default,
   211  			types.Uint(fnColTag), fnVal,
   212  			types.Uint(lnColTag), lnVal,
   213  			types.Uint(unusedTag), fnVal,
   214  		)
   215  
   216  		require.NoError(t, err)
   217  
   218  		vals, err := types.NewTuple(types.Format_Default,
   219  			types.Uint(addrColTag), addrVal,
   220  			types.Uint(ageColTag), ageVal,
   221  			types.Uint(titleColTag), titleVal,
   222  		)
   223  
   224  		require.NoError(t, err)
   225  
   226  		_, err = FromNoms(sch, keys, vals)
   227  		assert.Error(t, err)
   228  	})
   229  
   230  	t.Run("value tag in key", func(t *testing.T) {
   231  		keys, err := types.NewTuple(types.Format_Default,
   232  			types.Uint(fnColTag), fnVal,
   233  			types.Uint(lnColTag), lnVal,
   234  			types.Uint(ageColTag), ageVal,
   235  		)
   236  
   237  		require.NoError(t, err)
   238  
   239  		vals, err := types.NewTuple(types.Format_Default,
   240  			types.Uint(addrColTag), addrVal,
   241  			types.Uint(titleColTag), titleVal,
   242  		)
   243  
   244  		require.NoError(t, err)
   245  
   246  		_, err = FromNoms(sch, keys, vals)
   247  		assert.Error(t, err)
   248  	})
   249  }
   250  
   251  func TestSetColVal(t *testing.T) {
   252  	t.Run("valid update", func(t *testing.T) {
   253  		expected := map[uint64]types.Value{
   254  			lnColTag:    lnVal,
   255  			fnColTag:    fnVal,
   256  			ageColTag:   ageVal,
   257  			addrColTag:  addrVal,
   258  			titleColTag: titleVal}
   259  
   260  		updatedVal := types.String("sanchez")
   261  
   262  		r, err := newTestRow()
   263  		require.NoError(t, err)
   264  		r2, err := New(types.Format_Default, sch, expected)
   265  		require.NoError(t, err)
   266  		assert.Equal(t, r, r2)
   267  
   268  		updated, err := r.SetColVal(lnColTag, updatedVal, sch)
   269  		require.NoError(t, err)
   270  
   271  		// validate calling set does not mutate the original row
   272  		r3, err := New(types.Format_Default, sch, expected)
   273  		require.NoError(t, err)
   274  		assert.Equal(t, r, r3)
   275  		expected[lnColTag] = updatedVal
   276  		r4, err := New(types.Format_Default, sch, expected)
   277  		require.NoError(t, err)
   278  		assert.Equal(t, updated, r4)
   279  
   280  		// set to a nil value
   281  		updated, err = updated.SetColVal(titleColTag, nil, sch)
   282  		require.NoError(t, err)
   283  		delete(expected, titleColTag)
   284  		r5, err := New(types.Format_Default, sch, expected)
   285  		require.NoError(t, err)
   286  		assert.Equal(t, updated, r5)
   287  	})
   288  
   289  	t.Run("invalid update", func(t *testing.T) {
   290  		expected := map[uint64]types.Value{
   291  			lnColTag:    lnVal,
   292  			fnColTag:    fnVal,
   293  			ageColTag:   ageVal,
   294  			addrColTag:  addrVal,
   295  			titleColTag: titleVal}
   296  
   297  		r, err := newTestRow()
   298  		require.NoError(t, err)
   299  
   300  		r2, err := New(types.Format_Default, sch, expected)
   301  		require.NoError(t, err)
   302  		assert.Equal(t, r, r2)
   303  
   304  		// SetColVal allows an incorrect type to be set for a column
   305  		updatedRow, err := r.SetColVal(lnColTag, types.Bool(true), sch)
   306  		require.NoError(t, err)
   307  		// IsValid fails for the type problem
   308  		isv, err := IsValid(updatedRow, sch)
   309  		require.NoError(t, err)
   310  		assert.False(t, isv)
   311  		invalidCol, err := GetInvalidCol(updatedRow, sch)
   312  		require.NoError(t, err)
   313  		assert.NotNil(t, invalidCol)
   314  		assert.Equal(t, uint64(lnColTag), invalidCol.Tag)
   315  
   316  		// validate calling set does not mutate the original row
   317  		r3, err := New(types.Format_Default, sch, expected)
   318  		require.NoError(t, err)
   319  		assert.Equal(t, r, r3)
   320  	})
   321  }
   322  
   323  func TestConvToAndFromTuple(t *testing.T) {
   324  	ctx := context.Background()
   325  
   326  	r, err := newTestRow()
   327  	require.NoError(t, err)
   328  
   329  	keyTpl := r.NomsMapKey(sch).(TupleVals)
   330  	valTpl := r.NomsMapValue(sch).(TupleVals)
   331  	keyVal, err := keyTpl.Value(ctx)
   332  	require.NoError(t, err)
   333  	valVal, err := valTpl.Value(ctx)
   334  	require.NoError(t, err)
   335  	r2, err := FromNoms(sch, keyVal.(types.Tuple), valVal.(types.Tuple))
   336  	require.NoError(t, err)
   337  
   338  	fmt.Println(Fmt(context.Background(), r, sch))
   339  	fmt.Println(Fmt(context.Background(), r2, sch))
   340  
   341  	if !AreEqual(r, r2, sch) {
   342  		t.Error("Failed to convert to a noms tuple, and then convert back to the same row")
   343  	}
   344  }
   345  
   346  func TestReduceToIndex(t *testing.T) {
   347  	taggedValues := []struct {
   348  		row           TaggedValues
   349  		expectedIndex TaggedValues
   350  	}{
   351  		{
   352  			TaggedValues{
   353  				lnColTag:       types.String("yes"),
   354  				fnColTag:       types.String("no"),
   355  				addrColTag:     types.String("nonsense"),
   356  				ageColTag:      types.Uint(55),
   357  				titleColTag:    types.String("lol"),
   358  				reservedColTag: types.String("what"),
   359  			},
   360  			TaggedValues{
   361  				lnColTag:  types.String("yes"),
   362  				fnColTag:  types.String("no"),
   363  				ageColTag: types.Uint(55),
   364  			},
   365  		},
   366  		{
   367  			TaggedValues{
   368  				lnColTag:       types.String("yes"),
   369  				addrColTag:     types.String("nonsense"),
   370  				ageColTag:      types.Uint(55),
   371  				titleColTag:    types.String("lol"),
   372  				reservedColTag: types.String("what"),
   373  			},
   374  			TaggedValues{
   375  				lnColTag:  types.String("yes"),
   376  				ageColTag: types.Uint(55),
   377  			},
   378  		},
   379  		{
   380  			TaggedValues{
   381  				lnColTag: types.String("yes"),
   382  				fnColTag: types.String("no"),
   383  			},
   384  			TaggedValues{
   385  				lnColTag: types.String("yes"),
   386  				fnColTag: types.String("no"),
   387  			},
   388  		},
   389  		{
   390  			TaggedValues{
   391  				addrColTag:     types.String("nonsense"),
   392  				titleColTag:    types.String("lol"),
   393  				reservedColTag: types.String("what"),
   394  			},
   395  			TaggedValues{},
   396  		},
   397  	}
   398  
   399  	for _, tvCombo := range taggedValues {
   400  		row, err := New(types.Format_Default, sch, tvCombo.row)
   401  		require.NoError(t, err)
   402  		expectedIndex, err := New(types.Format_Default, index.Schema(), tvCombo.expectedIndex)
   403  		require.NoError(t, err)
   404  		indexRow, err := reduceToIndex(index, row)
   405  		require.NoError(t, err)
   406  		assert.True(t, AreEqual(expectedIndex, indexRow, index.Schema()))
   407  	}
   408  }
   409  
   410  func reduceToIndex(idx schema.Index, r Row) (Row, error) {
   411  	newRow := nomsRow{
   412  		key:   make(TaggedValues),
   413  		value: make(TaggedValues),
   414  		nbf:   r.Format(),
   415  	}
   416  	for _, tag := range idx.AllTags() {
   417  		if val, ok := r.GetColVal(tag); ok {
   418  			newRow.key[tag] = val
   419  		}
   420  	}
   421  
   422  	return newRow, nil
   423  }