github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/schema/index_test.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 schema 16 17 import ( 18 "testing" 19 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 23 "github.com/dolthub/dolt/go/store/types" 24 ) 25 26 func TestIndexCollectionAddIndex(t *testing.T) { 27 colColl := NewColCollection( 28 NewColumn("pk1", 1, types.IntKind, true, NotNullConstraint{}), 29 NewColumn("pk2", 2, types.IntKind, true, NotNullConstraint{}), 30 NewColumn("v1", 3, types.IntKind, false), 31 NewColumn("v2", 4, types.UintKind, false), 32 NewColumn("v3", 5, types.StringKind, false), 33 ) 34 indexColl := NewIndexCollection(colColl).(*indexCollectionImpl) 35 36 testIndexes := []*indexImpl{ 37 { 38 name: "idx_v1", 39 tags: []uint64{3}, 40 allTags: []uint64{3, 1, 2}, 41 indexColl: indexColl, 42 }, 43 { 44 name: "idx_v1v3v2", 45 tags: []uint64{3, 5, 4}, 46 allTags: []uint64{3, 5, 4, 1, 2}, 47 indexColl: indexColl, 48 comment: "hello there", 49 }, 50 { 51 name: "idx_pk1v1", 52 tags: []uint64{1, 3}, 53 allTags: []uint64{1, 3, 2}, 54 indexColl: indexColl, 55 }, 56 { 57 name: "idx_pk2pk1v2", 58 tags: []uint64{2, 1, 4}, 59 allTags: []uint64{2, 1, 4}, 60 indexColl: indexColl, 61 }, 62 } 63 64 for _, testIndex := range testIndexes { 65 t.Run(testIndex.Name(), func(t *testing.T) { 66 assert.False(t, indexColl.Contains(testIndex.Name())) 67 assert.False(t, indexColl.hasIndexOnColumns(testIndex.ColumnNames()...)) 68 assert.False(t, indexColl.hasIndexOnTags(testIndex.IndexedColumnTags()...)) 69 assert.Nil(t, indexColl.GetByName(testIndex.Name())) 70 71 indexColl.AddIndex(testIndex) 72 assert.Equal(t, testIndex, indexColl.GetByName(testIndex.Name())) 73 assert.Equal(t, []Index{testIndex}, indexColl.AllIndexes()) 74 for _, tag := range testIndex.IndexedColumnTags() { 75 assert.Equal(t, []Index{testIndex}, indexColl.IndexesWithTag(tag)) 76 } 77 for _, col := range testIndex.ColumnNames() { 78 assert.Equal(t, []Index{testIndex}, indexColl.IndexesWithColumn(col)) 79 } 80 assert.True(t, indexColl.Contains(testIndex.Name())) 81 assert.True(t, indexColl.hasIndexOnColumns(testIndex.ColumnNames()...)) 82 assert.True(t, indexColl.hasIndexOnTags(testIndex.IndexedColumnTags()...)) 83 }) 84 indexColl.clear(t) 85 } 86 87 const prefix = "new_" 88 89 t.Run("Tag Overwrites", func(t *testing.T) { 90 for _, testIndex := range testIndexes { 91 indexColl.AddIndex(testIndex) 92 newIndex := testIndex.copy() 93 newIndex.name = prefix + testIndex.name 94 indexColl.AddIndex(newIndex) 95 assert.Equal(t, newIndex, indexColl.GetByName(newIndex.Name())) 96 assert.Nil(t, indexColl.GetByName(testIndex.Name())) 97 assert.Contains(t, indexColl.AllIndexes(), newIndex) 98 assert.NotContains(t, indexColl.AllIndexes(), testIndex) 99 for _, tag := range newIndex.IndexedColumnTags() { 100 assert.Contains(t, indexColl.IndexesWithTag(tag), newIndex) 101 assert.NotContains(t, indexColl.IndexesWithTag(tag), testIndex) 102 } 103 for _, col := range newIndex.ColumnNames() { 104 assert.Contains(t, indexColl.IndexesWithColumn(col), newIndex) 105 assert.NotContains(t, indexColl.IndexesWithColumn(col), testIndex) 106 } 107 assert.True(t, indexColl.Contains(newIndex.Name())) 108 assert.False(t, indexColl.Contains(testIndex.Name())) 109 assert.True(t, indexColl.hasIndexOnColumns(newIndex.ColumnNames()...)) 110 assert.True(t, indexColl.hasIndexOnTags(newIndex.IndexedColumnTags()...)) 111 } 112 }) 113 114 t.Run("Name Overwrites", func(t *testing.T) { 115 // should be able to reduce collection to one index 116 lastStanding := &indexImpl{ 117 name: "none", 118 tags: []uint64{4}, 119 allTags: []uint64{4, 1, 2}, 120 indexColl: indexColl, 121 } 122 123 for _, testIndex := range testIndexes { 124 lastStanding.name = prefix + testIndex.name 125 indexColl.AddIndex(lastStanding) 126 } 127 128 assert.Equal(t, map[string]*indexImpl{lastStanding.name: lastStanding}, indexColl.indexes) 129 for tag, indexes := range indexColl.colTagToIndex { 130 if tag == 4 { 131 assert.Equal(t, indexes, []*indexImpl{lastStanding}) 132 } else { 133 assert.Empty(t, indexes) 134 } 135 } 136 }) 137 } 138 139 func TestIndexCollectionAddIndexByColNames(t *testing.T) { 140 colColl := NewColCollection( 141 NewColumn("pk1", 1, types.IntKind, true, NotNullConstraint{}), 142 NewColumn("pk2", 2, types.IntKind, true, NotNullConstraint{}), 143 NewColumn("v1", 3, types.IntKind, false), 144 NewColumn("v2", 4, types.UintKind, false), 145 NewColumn("v3", 5, types.StringKind, false), 146 ) 147 indexColl := NewIndexCollection(colColl).(*indexCollectionImpl) 148 149 testIndexes := []struct { 150 cols []string 151 index *indexImpl 152 }{ 153 { 154 []string{"v1"}, 155 &indexImpl{ 156 name: "idx_v1", 157 tags: []uint64{3}, 158 allTags: []uint64{3, 1, 2}, 159 indexColl: indexColl, 160 }, 161 }, 162 { 163 []string{"v1", "v3", "v2"}, 164 &indexImpl{ 165 name: "idx_v1v3v2", 166 tags: []uint64{3, 5, 4}, 167 allTags: []uint64{3, 5, 4, 1, 2}, 168 indexColl: indexColl, 169 }, 170 }, 171 { 172 []string{"pk1", "v1"}, 173 &indexImpl{ 174 name: "idx_pk1v1", 175 tags: []uint64{1, 3}, 176 allTags: []uint64{1, 3, 2}, 177 indexColl: indexColl, 178 comment: "hello there", 179 }, 180 }, 181 { 182 []string{"pk2", "pk1", "v2"}, 183 &indexImpl{ 184 name: "idx_pk2pk1v2", 185 tags: []uint64{2, 1, 4}, 186 allTags: []uint64{2, 1, 4}, 187 indexColl: indexColl, 188 }, 189 }, 190 } 191 192 for _, testIndex := range testIndexes { 193 t.Run(testIndex.index.Name(), func(t *testing.T) { 194 assert.False(t, indexColl.Contains(testIndex.index.Name())) 195 assert.False(t, indexColl.hasIndexOnColumns(testIndex.index.ColumnNames()...)) 196 assert.False(t, indexColl.hasIndexOnTags(testIndex.index.IndexedColumnTags()...)) 197 assert.Nil(t, indexColl.GetByName(testIndex.index.Name())) 198 199 resIndex, err := indexColl.AddIndexByColNames(testIndex.index.Name(), testIndex.cols, IndexProperties{IsUnique: testIndex.index.IsUnique(), Comment: testIndex.index.Comment()}) 200 assert.NoError(t, err) 201 assert.Equal(t, testIndex.index, resIndex) 202 assert.Equal(t, testIndex.index, indexColl.GetByName(resIndex.Name())) 203 assert.Equal(t, []Index{testIndex.index}, indexColl.AllIndexes()) 204 for _, tag := range resIndex.IndexedColumnTags() { 205 assert.Equal(t, []Index{resIndex}, indexColl.IndexesWithTag(tag)) 206 } 207 for _, col := range resIndex.ColumnNames() { 208 assert.Equal(t, []Index{resIndex}, indexColl.IndexesWithColumn(col)) 209 } 210 assert.True(t, indexColl.Contains(resIndex.Name())) 211 assert.True(t, indexColl.hasIndexOnColumns(resIndex.ColumnNames()...)) 212 assert.True(t, indexColl.hasIndexOnTags(resIndex.IndexedColumnTags()...)) 213 }) 214 indexColl.clear(t) 215 } 216 217 t.Run("Pre-existing", func(t *testing.T) { 218 for _, testIndex := range testIndexes { 219 _, err := indexColl.AddIndexByColNames(testIndex.index.Name(), testIndex.cols, IndexProperties{IsUnique: testIndex.index.IsUnique(), Comment: testIndex.index.Comment()}) 220 assert.NoError(t, err) 221 _, err = indexColl.AddIndexByColNames("nonsense", testIndex.cols, IndexProperties{IsUnique: testIndex.index.IsUnique(), Comment: testIndex.index.Comment()}) 222 assert.Error(t, err) 223 _, err = indexColl.AddIndexByColNames(testIndex.index.Name(), []string{"v2"}, IndexProperties{IsUnique: testIndex.index.IsUnique(), Comment: testIndex.index.Comment()}) 224 assert.Error(t, err) 225 } 226 indexColl.clear(t) 227 }) 228 229 t.Run("Non-existing Columns", func(t *testing.T) { 230 _, err := indexColl.AddIndexByColNames("nonsense", []string{"v4"}, IndexProperties{IsUnique: false, Comment: ""}) 231 assert.Error(t, err) 232 _, err = indexColl.AddIndexByColNames("nonsense", []string{"v1", "v2", "pk3"}, IndexProperties{IsUnique: false, Comment: ""}) 233 assert.Error(t, err) 234 }) 235 } 236 237 func TestIndexCollectionAddIndexByColTags(t *testing.T) { 238 colColl := NewColCollection( 239 NewColumn("pk1", 1, types.IntKind, true, NotNullConstraint{}), 240 NewColumn("pk2", 2, types.IntKind, true, NotNullConstraint{}), 241 NewColumn("v1", 3, types.IntKind, false), 242 NewColumn("v2", 4, types.UintKind, false), 243 NewColumn("v3", 5, types.StringKind, false), 244 ) 245 indexColl := NewIndexCollection(colColl).(*indexCollectionImpl) 246 247 testIndexes := []*indexImpl{ 248 { 249 name: "idx_v1", 250 tags: []uint64{3}, 251 allTags: []uint64{3, 1, 2}, 252 indexColl: indexColl, 253 comment: "hello there", 254 }, 255 { 256 name: "idx_v1v3v2", 257 tags: []uint64{3, 5, 4}, 258 allTags: []uint64{3, 5, 4, 1, 2}, 259 indexColl: indexColl, 260 }, 261 { 262 name: "idx_pk1v1", 263 tags: []uint64{1, 3}, 264 allTags: []uint64{1, 3, 2}, 265 indexColl: indexColl, 266 }, 267 { 268 name: "idx_pk2pk1v2", 269 tags: []uint64{2, 1, 4}, 270 allTags: []uint64{2, 1, 4}, 271 indexColl: indexColl, 272 }, 273 } 274 275 for _, testIndex := range testIndexes { 276 t.Run(testIndex.Name(), func(t *testing.T) { 277 assert.False(t, indexColl.Contains(testIndex.Name())) 278 assert.False(t, indexColl.hasIndexOnColumns(testIndex.ColumnNames()...)) 279 assert.False(t, indexColl.hasIndexOnTags(testIndex.IndexedColumnTags()...)) 280 assert.Nil(t, indexColl.GetByName(testIndex.Name())) 281 282 resIndex, err := indexColl.AddIndexByColTags(testIndex.Name(), testIndex.tags, IndexProperties{IsUnique: testIndex.IsUnique(), Comment: testIndex.Comment()}) 283 assert.NoError(t, err) 284 assert.Equal(t, testIndex, resIndex) 285 assert.Equal(t, testIndex, indexColl.GetByName(resIndex.Name())) 286 assert.Equal(t, []Index{testIndex}, indexColl.AllIndexes()) 287 for _, tag := range resIndex.IndexedColumnTags() { 288 assert.Equal(t, []Index{resIndex}, indexColl.IndexesWithTag(tag)) 289 } 290 for _, col := range resIndex.ColumnNames() { 291 assert.Equal(t, []Index{resIndex}, indexColl.IndexesWithColumn(col)) 292 } 293 assert.True(t, indexColl.Contains(resIndex.Name())) 294 assert.True(t, indexColl.hasIndexOnColumns(resIndex.ColumnNames()...)) 295 assert.True(t, indexColl.hasIndexOnTags(resIndex.IndexedColumnTags()...)) 296 }) 297 indexColl.clear(t) 298 } 299 300 t.Run("Pre-existing", func(t *testing.T) { 301 for _, testIndex := range testIndexes { 302 _, err := indexColl.AddIndexByColTags(testIndex.Name(), testIndex.tags, IndexProperties{IsUnique: testIndex.IsUnique(), Comment: testIndex.Comment()}) 303 assert.NoError(t, err) 304 _, err = indexColl.AddIndexByColTags("nonsense", testIndex.tags, IndexProperties{IsUnique: testIndex.IsUnique(), Comment: testIndex.Comment()}) 305 assert.Error(t, err) 306 _, err = indexColl.AddIndexByColTags(testIndex.Name(), []uint64{4}, IndexProperties{IsUnique: testIndex.IsUnique(), Comment: testIndex.Comment()}) 307 assert.Error(t, err) 308 } 309 indexColl.clear(t) 310 }) 311 312 t.Run("Non-existing Tags", func(t *testing.T) { 313 _, err := indexColl.AddIndexByColTags("nonsense", []uint64{6}, IndexProperties{IsUnique: false, Comment: ""}) 314 assert.Error(t, err) 315 _, err = indexColl.AddIndexByColTags("nonsense", []uint64{3, 4, 10}, IndexProperties{IsUnique: false, Comment: ""}) 316 assert.Error(t, err) 317 }) 318 } 319 320 func TestIndexCollectionAllIndexes(t *testing.T) { 321 colColl := NewColCollection( 322 NewColumn("pk1", 1, types.IntKind, true, NotNullConstraint{}), 323 NewColumn("pk2", 2, types.IntKind, true, NotNullConstraint{}), 324 NewColumn("v1", 3, types.IntKind, false), 325 NewColumn("v2", 4, types.UintKind, false), 326 NewColumn("v3", 5, types.StringKind, false), 327 ) 328 indexColl := NewIndexCollection(colColl).(*indexCollectionImpl) 329 330 indexColl.AddIndex(&indexImpl{ 331 name: "idx_z", 332 tags: []uint64{3}, 333 }) 334 _, err := indexColl.AddIndexByColNames("idx_a", []string{"v2"}, IndexProperties{IsUnique: false, Comment: ""}) 335 require.NoError(t, err) 336 _, err = indexColl.AddIndexByColTags("idx_n", []uint64{5}, IndexProperties{IsUnique: false, Comment: "hello there"}) 337 require.NoError(t, err) 338 339 assert.Equal(t, []Index{ 340 &indexImpl{ 341 name: "idx_a", 342 tags: []uint64{4}, 343 allTags: []uint64{4, 1, 2}, 344 indexColl: indexColl, 345 isUnique: false, 346 comment: "", 347 }, 348 &indexImpl{ 349 name: "idx_n", 350 tags: []uint64{5}, 351 allTags: []uint64{5, 1, 2}, 352 indexColl: indexColl, 353 isUnique: false, 354 comment: "hello there", 355 }, 356 &indexImpl{ 357 name: "idx_z", 358 tags: []uint64{3}, 359 allTags: []uint64{3, 1, 2}, 360 indexColl: indexColl, 361 isUnique: false, 362 comment: "", 363 }, 364 }, indexColl.AllIndexes()) 365 } 366 367 func TestIndexCollectionRemoveIndex(t *testing.T) { 368 colColl := NewColCollection( 369 NewColumn("pk1", 1, types.IntKind, true, NotNullConstraint{}), 370 NewColumn("pk2", 2, types.IntKind, true, NotNullConstraint{}), 371 NewColumn("v1", 3, types.IntKind, false), 372 NewColumn("v2", 4, types.UintKind, false), 373 NewColumn("v3", 5, types.StringKind, false), 374 ) 375 indexColl := NewIndexCollection(colColl).(*indexCollectionImpl) 376 377 testIndexes := []Index{ 378 &indexImpl{ 379 name: "idx_v1", 380 tags: []uint64{3}, 381 allTags: []uint64{3, 1, 2}, 382 indexColl: indexColl, 383 }, 384 &indexImpl{ 385 name: "idx_v1v3v2", 386 tags: []uint64{3, 5, 4}, 387 allTags: []uint64{3, 5, 4, 1, 2}, 388 indexColl: indexColl, 389 comment: "hello there", 390 }, 391 &indexImpl{ 392 name: "idx_pk1v1", 393 tags: []uint64{1, 3}, 394 allTags: []uint64{1, 3, 2}, 395 indexColl: indexColl, 396 }, 397 &indexImpl{ 398 name: "idx_pk2pk1v2", 399 tags: []uint64{2, 1, 4}, 400 allTags: []uint64{2, 1, 4}, 401 indexColl: indexColl, 402 }, 403 } 404 indexColl.AddIndex(testIndexes...) 405 406 for _, testIndex := range testIndexes { 407 resIndex, err := indexColl.RemoveIndex(testIndex.Name()) 408 assert.NoError(t, err) 409 assert.Equal(t, testIndex, resIndex) 410 assert.NotContains(t, indexColl.indexes, resIndex.Name()) 411 assert.NotContains(t, indexColl.indexes, resIndex) 412 for _, indexes := range indexColl.colTagToIndex { 413 assert.NotContains(t, indexes, resIndex) 414 } 415 _, err = indexColl.RemoveIndex(testIndex.Name()) 416 assert.Error(t, err) 417 } 418 } 419 420 func TestIndexCollectionRenameIndex(t *testing.T) { 421 colColl := NewColCollection( 422 NewColumn("pk1", 1, types.IntKind, true, NotNullConstraint{}), 423 NewColumn("pk2", 2, types.IntKind, true, NotNullConstraint{}), 424 NewColumn("v1", 3, types.IntKind, false), 425 NewColumn("v2", 4, types.UintKind, false), 426 NewColumn("v3", 5, types.StringKind, false), 427 ) 428 indexColl := NewIndexCollection(colColl).(*indexCollectionImpl) 429 index := &indexImpl{ 430 name: "idx_a", 431 tags: []uint64{3}, 432 allTags: []uint64{3, 1, 2}, 433 indexColl: indexColl, 434 } 435 indexColl.AddIndex(index) 436 437 const newIndexName = "idx_newname" 438 expectedIndex := index.copy() 439 expectedIndex.name = newIndexName 440 441 resIndex, err := indexColl.RenameIndex(index.Name(), newIndexName) 442 newIndex := resIndex.(*indexImpl) 443 assert.NoError(t, err) 444 assert.Equal(t, expectedIndex, resIndex) 445 assert.Equal(t, indexColl.indexes, map[string]*indexImpl{newIndexName: newIndex}) 446 for tag, indexes := range indexColl.colTagToIndex { 447 if tag == 3 { 448 assert.Equal(t, indexes, []*indexImpl{newIndex}) 449 } else { 450 assert.Empty(t, indexes) 451 } 452 } 453 454 indexColl.AddIndex(index) 455 _, err = indexColl.RenameIndex(newIndexName, index.Name()) 456 assert.Error(t, err) 457 } 458 459 func (ixc *indexCollectionImpl) clear(_ *testing.T) { 460 ixc.indexes = make(map[string]*indexImpl) 461 for key := range ixc.colTagToIndex { 462 ixc.colTagToIndex[key] = nil 463 } 464 }