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 }