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  }