vitess.io/vitess@v0.16.2/go/vt/vttablet/onlineddl/vrepl/unique_key_test.go (about)

     1  /*
     2     Copyright 2016 GitHub Inc.
     3  	 See https://github.com/github/gh-ost/blob/master/LICENSE
     4  */
     5  /*
     6  Copyright 2021 The Vitess Authors.
     7  
     8  Licensed under the Apache License, Version 2.0 (the "License");
     9  you may not use this file except in compliance with the License.
    10  You may obtain a copy of the License at
    11  
    12      http://www.apache.org/licenses/LICENSE-2.0
    13  
    14  Unless required by applicable law or agreed to in writing, software
    15  distributed under the License is distributed on an "AS IS" BASIS,
    16  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    17  See the License for the specific language governing permissions and
    18  limitations under the License.
    19  */
    20  
    21  package vrepl
    22  
    23  import (
    24  	"testing"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  )
    28  
    29  var (
    30  	columns1   = ParseColumnList("c1")
    31  	columns12  = ParseColumnList("c1,c2")
    32  	columns123 = ParseColumnList("c1,c2,c3")
    33  	columns2   = ParseColumnList("c2")
    34  	columns21  = ParseColumnList("c2,c1")
    35  	columns12A = ParseColumnList("c1,c2,ca")
    36  )
    37  
    38  func TestGetSharedUniqueKeys(t *testing.T) {
    39  	tt := []struct {
    40  		name                           string
    41  		sourceUKs, targetUKs           [](*UniqueKey)
    42  		renameMap                      map[string]string
    43  		expectSourceUK, expectTargetUK *UniqueKey
    44  	}{
    45  		{
    46  			name:           "empty",
    47  			sourceUKs:      []*UniqueKey{},
    48  			targetUKs:      []*UniqueKey{},
    49  			renameMap:      map[string]string{},
    50  			expectSourceUK: nil,
    51  			expectTargetUK: nil,
    52  		},
    53  		{
    54  			name: "half empty",
    55  			sourceUKs: []*UniqueKey{
    56  				{Name: "PRIMARY", Columns: *columns1},
    57  			},
    58  			targetUKs:      []*UniqueKey{},
    59  			renameMap:      map[string]string{},
    60  			expectSourceUK: nil,
    61  			expectTargetUK: nil,
    62  		},
    63  		{
    64  			name: "single identical",
    65  			sourceUKs: []*UniqueKey{
    66  				{Name: "PRIMARY", Columns: *columns1},
    67  			},
    68  			targetUKs: []*UniqueKey{
    69  				{Name: "PRIMARY", Columns: *columns1},
    70  			},
    71  			renameMap:      map[string]string{},
    72  			expectSourceUK: &UniqueKey{Name: "PRIMARY", Columns: *columns1},
    73  			expectTargetUK: &UniqueKey{Name: "PRIMARY", Columns: *columns1},
    74  		},
    75  		{
    76  			name: "single identical non pk",
    77  			sourceUKs: []*UniqueKey{
    78  				{Name: "uidx", Columns: *columns1},
    79  			},
    80  			targetUKs: []*UniqueKey{
    81  				{Name: "uidx", Columns: *columns1},
    82  			},
    83  			renameMap:      map[string]string{},
    84  			expectSourceUK: &UniqueKey{Name: "uidx", Columns: *columns1},
    85  			expectTargetUK: &UniqueKey{Name: "uidx", Columns: *columns1},
    86  		},
    87  		{
    88  			name: "single identical, source is nullable",
    89  			sourceUKs: []*UniqueKey{
    90  				{Name: "uidx", Columns: *columns1, HasNullable: true},
    91  			},
    92  			targetUKs: []*UniqueKey{
    93  				{Name: "uidx", Columns: *columns1},
    94  			},
    95  			renameMap:      map[string]string{},
    96  			expectSourceUK: nil,
    97  			expectTargetUK: nil,
    98  		},
    99  		{
   100  			name: "single identical, target is nullable",
   101  			sourceUKs: []*UniqueKey{
   102  				{Name: "uidx", Columns: *columns1},
   103  			},
   104  			targetUKs: []*UniqueKey{
   105  				{Name: "uidx", Columns: *columns1, HasNullable: true},
   106  			},
   107  			renameMap:      map[string]string{},
   108  			expectSourceUK: nil,
   109  			expectTargetUK: nil,
   110  		},
   111  		{
   112  			name: "single no shared",
   113  			sourceUKs: []*UniqueKey{
   114  				{Name: "uidx", Columns: *columns1},
   115  			},
   116  			targetUKs: []*UniqueKey{
   117  				{Name: "uidx", Columns: *columns12},
   118  			},
   119  			renameMap:      map[string]string{},
   120  			expectSourceUK: nil,
   121  			expectTargetUK: nil,
   122  		},
   123  		{
   124  			name: "single no shared different order",
   125  			sourceUKs: []*UniqueKey{
   126  				{Name: "uidx", Columns: *columns12},
   127  			},
   128  			targetUKs: []*UniqueKey{
   129  				{Name: "uidx", Columns: *columns21},
   130  			},
   131  			renameMap:      map[string]string{},
   132  			expectSourceUK: nil,
   133  			expectTargetUK: nil,
   134  		},
   135  		{
   136  			name: "single identical, source has FLOAT",
   137  			sourceUKs: []*UniqueKey{
   138  				{Name: "uidx", Columns: *columns1, HasFloat: true},
   139  			},
   140  			targetUKs: []*UniqueKey{
   141  				{Name: "uidx", Columns: *columns1},
   142  			},
   143  			renameMap:      map[string]string{},
   144  			expectSourceUK: nil,
   145  			expectTargetUK: nil,
   146  		},
   147  		{
   148  			name: "exact match",
   149  			sourceUKs: []*UniqueKey{
   150  				{Name: "uidx", Columns: *columns1},
   151  				{Name: "uidx123", Columns: *columns123},
   152  			},
   153  			targetUKs: []*UniqueKey{
   154  				{Name: "uidx", Columns: *columns12},
   155  				{Name: "uidx123", Columns: *columns123},
   156  			},
   157  			renameMap:      map[string]string{},
   158  			expectSourceUK: &UniqueKey{Name: "uidx123", Columns: *columns123},
   159  			expectTargetUK: &UniqueKey{Name: "uidx123", Columns: *columns123},
   160  		},
   161  		{
   162  			name: "exact match from multiple options",
   163  			sourceUKs: []*UniqueKey{
   164  				{Name: "uidx", Columns: *columns1},
   165  				{Name: "uidx123", Columns: *columns123},
   166  				{Name: "uidx12", Columns: *columns12},
   167  			},
   168  			targetUKs: []*UniqueKey{
   169  				{Name: "uidx12", Columns: *columns12},
   170  				{Name: "uidx", Columns: *columns12},
   171  				{Name: "uidx123", Columns: *columns123},
   172  			},
   173  			renameMap:      map[string]string{},
   174  			expectSourceUK: &UniqueKey{Name: "uidx123", Columns: *columns123},
   175  			expectTargetUK: &UniqueKey{Name: "uidx123", Columns: *columns123},
   176  		},
   177  		{
   178  			name: "exact match from multiple options reorder",
   179  			sourceUKs: []*UniqueKey{
   180  				{Name: "uidx12", Columns: *columns12},
   181  				{Name: "uidx", Columns: *columns1},
   182  				{Name: "uidx123", Columns: *columns123},
   183  			},
   184  			targetUKs: []*UniqueKey{
   185  				{Name: "uidx", Columns: *columns21},
   186  				{Name: "uidx123", Columns: *columns123},
   187  				{Name: "uidx12", Columns: *columns12},
   188  			},
   189  			renameMap:      map[string]string{},
   190  			expectSourceUK: &UniqueKey{Name: "uidx12", Columns: *columns12},
   191  			expectTargetUK: &UniqueKey{Name: "uidx12", Columns: *columns12},
   192  		},
   193  		{
   194  			name: "match different names",
   195  			sourceUKs: []*UniqueKey{
   196  				{Name: "uidx1", Columns: *columns1},
   197  				{Name: "uidx12", Columns: *columns12},
   198  				{Name: "uidx123", Columns: *columns123},
   199  			},
   200  			targetUKs: []*UniqueKey{
   201  				{Name: "uidx21", Columns: *columns21},
   202  				{Name: "uidx123", Columns: *columns123},
   203  				{Name: "uidxother", Columns: *columns12},
   204  			},
   205  			renameMap:      map[string]string{},
   206  			expectSourceUK: &UniqueKey{Name: "uidx12", Columns: *columns12},
   207  			expectTargetUK: &UniqueKey{Name: "uidxother", Columns: *columns12},
   208  		},
   209  		{
   210  			name: "match different names, nullable",
   211  			sourceUKs: []*UniqueKey{
   212  				{Name: "uidx1", Columns: *columns1},
   213  				{Name: "uidx12", Columns: *columns12},
   214  				{Name: "uidx123", Columns: *columns123},
   215  			},
   216  			targetUKs: []*UniqueKey{
   217  				{Name: "uidx21", Columns: *columns21},
   218  				{Name: "uidx123other", Columns: *columns123},
   219  				{Name: "uidx12", Columns: *columns12, HasNullable: true},
   220  			},
   221  			renameMap:      map[string]string{},
   222  			expectSourceUK: &UniqueKey{Name: "uidx123", Columns: *columns123},
   223  			expectTargetUK: &UniqueKey{Name: "uidx123other", Columns: *columns123},
   224  		},
   225  		{
   226  			name: "match different column names",
   227  			sourceUKs: []*UniqueKey{
   228  				{Name: "uidx1", Columns: *columns1},
   229  				{Name: "uidx12", Columns: *columns12},
   230  				{Name: "uidx123", Columns: *columns123},
   231  			},
   232  			targetUKs: []*UniqueKey{
   233  				{Name: "uidx21", Columns: *columns21},
   234  				{Name: "uidx12A", Columns: *columns12A},
   235  			},
   236  			renameMap:      map[string]string{"c3": "ca"},
   237  			expectSourceUK: &UniqueKey{Name: "uidx123", Columns: *columns123},
   238  			expectTargetUK: &UniqueKey{Name: "uidx12A", Columns: *columns12A},
   239  		},
   240  		{
   241  			// enforce mapping from c3 to ca; will not match c3<->c3
   242  			name: "no match identical column names",
   243  			sourceUKs: []*UniqueKey{
   244  				{Name: "uidx1", Columns: *columns1},
   245  				{Name: "uidx12", Columns: *columns12},
   246  				{Name: "uidx123", Columns: *columns123},
   247  			},
   248  			targetUKs: []*UniqueKey{
   249  				{Name: "uidx21", Columns: *columns21},
   250  				{Name: "uidx123", Columns: *columns123},
   251  			},
   252  			renameMap:      map[string]string{"c3": "ca"},
   253  			expectSourceUK: nil,
   254  			expectTargetUK: nil,
   255  		},
   256  		{
   257  			name: "no match different column names",
   258  			sourceUKs: []*UniqueKey{
   259  				{Name: "uidx1", Columns: *columns1},
   260  				{Name: "uidx12", Columns: *columns12},
   261  				{Name: "uidx123", Columns: *columns123},
   262  			},
   263  			targetUKs: []*UniqueKey{
   264  				{Name: "uidx21", Columns: *columns21},
   265  				{Name: "uidx12A", Columns: *columns12A},
   266  			},
   267  			renameMap:      map[string]string{"c3": "cx"},
   268  			expectSourceUK: nil,
   269  			expectTargetUK: nil,
   270  		},
   271  	}
   272  
   273  	for _, tc := range tt {
   274  		t.Run(tc.name, func(t *testing.T) {
   275  			sourceUK, targetUK := GetSharedUniqueKeys(tc.sourceUKs, tc.targetUKs, tc.renameMap)
   276  			assert.Equal(t, tc.expectSourceUK, sourceUK)
   277  			assert.Equal(t, tc.expectTargetUK, targetUK)
   278  		})
   279  	}
   280  }
   281  
   282  func TestAddedUniqueKeys(t *testing.T) {
   283  	emptyUniqueKeys := []*UniqueKey{}
   284  	tt := []struct {
   285  		name                 string
   286  		sourceUKs, targetUKs [](*UniqueKey)
   287  		renameMap            map[string]string
   288  		expectAddedUKs       [](*UniqueKey)
   289  		expectRemovedUKs     [](*UniqueKey)
   290  	}{
   291  		{
   292  			name:             "empty",
   293  			sourceUKs:        emptyUniqueKeys,
   294  			targetUKs:        emptyUniqueKeys,
   295  			renameMap:        map[string]string{},
   296  			expectAddedUKs:   emptyUniqueKeys,
   297  			expectRemovedUKs: emptyUniqueKeys,
   298  		},
   299  		{
   300  			name: "UK removed",
   301  			sourceUKs: []*UniqueKey{
   302  				{Name: "PRIMARY", Columns: *columns1},
   303  			},
   304  			targetUKs:      emptyUniqueKeys,
   305  			renameMap:      map[string]string{},
   306  			expectAddedUKs: emptyUniqueKeys,
   307  			expectRemovedUKs: []*UniqueKey{
   308  				{Name: "PRIMARY", Columns: *columns1},
   309  			},
   310  		},
   311  		{
   312  			name: "NULLable UK removed",
   313  			sourceUKs: []*UniqueKey{
   314  				{Name: "PRIMARY", Columns: *columns1, HasNullable: true},
   315  			},
   316  			targetUKs:      emptyUniqueKeys,
   317  			renameMap:      map[string]string{},
   318  			expectAddedUKs: emptyUniqueKeys,
   319  			expectRemovedUKs: []*UniqueKey{
   320  				{Name: "PRIMARY", Columns: *columns1, HasNullable: true},
   321  			},
   322  		},
   323  		{
   324  			name:      "UK added",
   325  			sourceUKs: emptyUniqueKeys,
   326  			targetUKs: []*UniqueKey{
   327  				{Name: "PRIMARY", Columns: *columns1},
   328  			},
   329  			renameMap: map[string]string{},
   330  			expectAddedUKs: []*UniqueKey{
   331  				{Name: "PRIMARY", Columns: *columns1},
   332  			},
   333  			expectRemovedUKs: emptyUniqueKeys,
   334  		},
   335  		{
   336  			name: "single identical",
   337  			sourceUKs: []*UniqueKey{
   338  				{Name: "PRIMARY", Columns: *columns1},
   339  			},
   340  			targetUKs: []*UniqueKey{
   341  				{Name: "PRIMARY", Columns: *columns1},
   342  			},
   343  			renameMap:        map[string]string{},
   344  			expectAddedUKs:   emptyUniqueKeys,
   345  			expectRemovedUKs: emptyUniqueKeys,
   346  		},
   347  		{
   348  			name: "single identical non pk",
   349  			sourceUKs: []*UniqueKey{
   350  				{Name: "uidx", Columns: *columns1},
   351  			},
   352  			targetUKs: []*UniqueKey{
   353  				{Name: "uidx", Columns: *columns1},
   354  			},
   355  			renameMap:        map[string]string{},
   356  			expectAddedUKs:   emptyUniqueKeys,
   357  			expectRemovedUKs: emptyUniqueKeys,
   358  		},
   359  		{
   360  			name: "single identical, source is nullable",
   361  			sourceUKs: []*UniqueKey{
   362  				{Name: "uidx", Columns: *columns1, HasNullable: true},
   363  			},
   364  			targetUKs: []*UniqueKey{
   365  				{Name: "uidx", Columns: *columns1},
   366  			},
   367  			renameMap:        map[string]string{},
   368  			expectAddedUKs:   emptyUniqueKeys,
   369  			expectRemovedUKs: emptyUniqueKeys,
   370  		},
   371  		{
   372  			name: "single identical, target is nullable",
   373  			sourceUKs: []*UniqueKey{
   374  				{Name: "uidx", Columns: *columns1},
   375  			},
   376  			targetUKs: []*UniqueKey{
   377  				{Name: "uidx", Columns: *columns1, HasNullable: true},
   378  			},
   379  			renameMap:        map[string]string{},
   380  			expectAddedUKs:   emptyUniqueKeys,
   381  			expectRemovedUKs: emptyUniqueKeys,
   382  		},
   383  		{
   384  			name: "expand columns: not considered added",
   385  			sourceUKs: []*UniqueKey{
   386  				{Name: "uidx", Columns: *columns1},
   387  			},
   388  			targetUKs: []*UniqueKey{
   389  				{Name: "uidx", Columns: *columns12},
   390  			},
   391  			renameMap:      map[string]string{},
   392  			expectAddedUKs: emptyUniqueKeys,
   393  			expectRemovedUKs: []*UniqueKey{
   394  				{Name: "uidx", Columns: *columns1},
   395  			},
   396  		},
   397  		{
   398  			name: "expand columns, different order: not considered added",
   399  			sourceUKs: []*UniqueKey{
   400  				{Name: "uidx", Columns: *columns1},
   401  			},
   402  			targetUKs: []*UniqueKey{
   403  				{Name: "uidx", Columns: *columns21},
   404  			},
   405  			renameMap:      map[string]string{},
   406  			expectAddedUKs: emptyUniqueKeys,
   407  			expectRemovedUKs: []*UniqueKey{
   408  				{Name: "uidx", Columns: *columns1},
   409  			},
   410  		},
   411  		{
   412  			name: "reduced columns: considered added",
   413  			sourceUKs: []*UniqueKey{
   414  				{Name: "uidx", Columns: *columns12},
   415  			},
   416  			targetUKs: []*UniqueKey{
   417  				{Name: "uidx", Columns: *columns1},
   418  			},
   419  			renameMap: map[string]string{},
   420  			expectAddedUKs: []*UniqueKey{
   421  				{Name: "uidx", Columns: *columns1},
   422  			},
   423  			expectRemovedUKs: emptyUniqueKeys,
   424  		},
   425  		{
   426  			name: "reduced columns, multiple: considered added",
   427  			sourceUKs: []*UniqueKey{
   428  				{Name: "uidx12", Columns: *columns12},
   429  				{Name: "uidx123", Columns: *columns123},
   430  				{Name: "uidx2", Columns: *columns2},
   431  			},
   432  			targetUKs: []*UniqueKey{
   433  				{Name: "uidx", Columns: *columns1},
   434  			},
   435  			renameMap: map[string]string{},
   436  			expectAddedUKs: []*UniqueKey{
   437  				{Name: "uidx", Columns: *columns1},
   438  			},
   439  			expectRemovedUKs: []*UniqueKey{
   440  				{Name: "uidx2", Columns: *columns2},
   441  			},
   442  		},
   443  		{
   444  			name: "different order: not considered added",
   445  			sourceUKs: []*UniqueKey{
   446  				{Name: "uidx", Columns: *columns12},
   447  			},
   448  			targetUKs: []*UniqueKey{
   449  				{Name: "uidx", Columns: *columns21},
   450  			},
   451  			renameMap:        map[string]string{},
   452  			expectAddedUKs:   emptyUniqueKeys,
   453  			expectRemovedUKs: emptyUniqueKeys,
   454  		},
   455  		{
   456  			name: "no match, different columns",
   457  			sourceUKs: []*UniqueKey{
   458  				{Name: "uidx1", Columns: *columns1},
   459  			},
   460  			targetUKs: []*UniqueKey{
   461  				{Name: "uidx2", Columns: *columns2},
   462  			},
   463  			renameMap: map[string]string{},
   464  			expectAddedUKs: []*UniqueKey{
   465  				{Name: "uidx2", Columns: *columns2},
   466  			},
   467  			expectRemovedUKs: []*UniqueKey{
   468  				{Name: "uidx1", Columns: *columns1},
   469  			},
   470  		},
   471  		{
   472  			name: "one match, one expand",
   473  			sourceUKs: []*UniqueKey{
   474  				{Name: "uidx", Columns: *columns1},
   475  				{Name: "uidx123", Columns: *columns123},
   476  			},
   477  			targetUKs: []*UniqueKey{
   478  				{Name: "uidx", Columns: *columns12},
   479  				{Name: "uidx123", Columns: *columns123},
   480  			},
   481  			renameMap:      map[string]string{},
   482  			expectAddedUKs: emptyUniqueKeys,
   483  			expectRemovedUKs: []*UniqueKey{
   484  				{Name: "uidx", Columns: *columns1},
   485  			},
   486  		},
   487  		{
   488  			name: "exact match from multiple options",
   489  			sourceUKs: []*UniqueKey{
   490  				{Name: "uidx", Columns: *columns1},
   491  				{Name: "uidx123", Columns: *columns123},
   492  				{Name: "uidx12", Columns: *columns12},
   493  			},
   494  			targetUKs: []*UniqueKey{
   495  				{Name: "uidx12", Columns: *columns12},
   496  				{Name: "uidx", Columns: *columns12},
   497  				{Name: "uidx123", Columns: *columns123},
   498  			},
   499  			renameMap:      map[string]string{},
   500  			expectAddedUKs: emptyUniqueKeys,
   501  			expectRemovedUKs: []*UniqueKey{
   502  				{Name: "uidx", Columns: *columns1},
   503  			},
   504  		},
   505  		{
   506  			name: "exact match from multiple options reorder",
   507  			sourceUKs: []*UniqueKey{
   508  				{Name: "uidx12", Columns: *columns12},
   509  				{Name: "uidx", Columns: *columns1},
   510  				{Name: "uidx123", Columns: *columns123},
   511  			},
   512  			targetUKs: []*UniqueKey{
   513  				{Name: "uidx", Columns: *columns21},
   514  				{Name: "uidx123", Columns: *columns123},
   515  				{Name: "uidx12", Columns: *columns12},
   516  			},
   517  			renameMap:      map[string]string{},
   518  			expectAddedUKs: emptyUniqueKeys,
   519  			expectRemovedUKs: []*UniqueKey{
   520  				{Name: "uidx", Columns: *columns1},
   521  			},
   522  		},
   523  		{
   524  			name: "match different names",
   525  			sourceUKs: []*UniqueKey{
   526  				{Name: "uidx1", Columns: *columns1},
   527  				{Name: "uidx12", Columns: *columns12},
   528  				{Name: "uidx123", Columns: *columns123},
   529  			},
   530  			targetUKs: []*UniqueKey{
   531  				{Name: "uidx21", Columns: *columns21},
   532  				{Name: "uidx123", Columns: *columns123},
   533  				{Name: "uidxother", Columns: *columns12},
   534  			},
   535  			renameMap:      map[string]string{},
   536  			expectAddedUKs: emptyUniqueKeys,
   537  			expectRemovedUKs: []*UniqueKey{
   538  				{Name: "uidx1", Columns: *columns1},
   539  			},
   540  		},
   541  		{
   542  			name: "match different names, nullable",
   543  			sourceUKs: []*UniqueKey{
   544  				{Name: "uidx1", Columns: *columns1},
   545  				{Name: "uidx12", Columns: *columns12},
   546  				{Name: "uidx123", Columns: *columns123},
   547  			},
   548  			targetUKs: []*UniqueKey{
   549  				{Name: "uidx21", Columns: *columns21},
   550  				{Name: "uidx123other", Columns: *columns123},
   551  				{Name: "uidx12", Columns: *columns12, HasNullable: true},
   552  			},
   553  			renameMap:      map[string]string{},
   554  			expectAddedUKs: emptyUniqueKeys,
   555  			expectRemovedUKs: []*UniqueKey{
   556  				{Name: "uidx1", Columns: *columns1},
   557  			},
   558  		},
   559  		{
   560  			name: "match different column names, expand",
   561  			sourceUKs: []*UniqueKey{
   562  				{Name: "uidx1", Columns: *columns1},
   563  				{Name: "uidx12", Columns: *columns12},
   564  				{Name: "uidx123", Columns: *columns123},
   565  			},
   566  			targetUKs: []*UniqueKey{
   567  				{Name: "uidx21", Columns: *columns21},
   568  				{Name: "uidx12A", Columns: *columns12A},
   569  			},
   570  			renameMap:      map[string]string{"c3": "ca"},
   571  			expectAddedUKs: emptyUniqueKeys,
   572  			expectRemovedUKs: []*UniqueKey{
   573  				{Name: "uidx1", Columns: *columns1},
   574  			},
   575  		},
   576  		{
   577  			name: "match different column names, no expand",
   578  			sourceUKs: []*UniqueKey{
   579  				{Name: "uidx123", Columns: *columns123},
   580  			},
   581  			targetUKs: []*UniqueKey{
   582  				{Name: "uidx12A", Columns: *columns12A},
   583  			},
   584  			renameMap:        map[string]string{"c3": "ca"},
   585  			expectAddedUKs:   emptyUniqueKeys,
   586  			expectRemovedUKs: emptyUniqueKeys,
   587  		},
   588  		{
   589  			// enforce mapping from c3 to ca; will not match c3<->c3
   590  			name: "no match identical column names, expand",
   591  			sourceUKs: []*UniqueKey{
   592  				{Name: "uidx1", Columns: *columns1},
   593  				{Name: "uidx12", Columns: *columns12},
   594  				{Name: "uidx123", Columns: *columns123},
   595  			},
   596  			targetUKs: []*UniqueKey{
   597  				{Name: "uidx21", Columns: *columns21},
   598  				{Name: "uidx123", Columns: *columns123},
   599  			},
   600  			renameMap: map[string]string{"c3": "ca"},
   601  			// 123 expands 12, so even though 3 is mapped to A, 123 is still not more constrained.
   602  			expectAddedUKs: emptyUniqueKeys,
   603  			expectRemovedUKs: []*UniqueKey{
   604  				{Name: "uidx1", Columns: *columns1},
   605  			},
   606  		},
   607  		{
   608  			// enforce mapping from c3 to ca; will not match c3<->c3
   609  			name: "no match identical column names, no expand",
   610  			sourceUKs: []*UniqueKey{
   611  				{Name: "uidx123", Columns: *columns123},
   612  			},
   613  			targetUKs: []*UniqueKey{
   614  				{Name: "uidx123", Columns: *columns123},
   615  			},
   616  			renameMap: map[string]string{"c3": "ca"},
   617  			expectAddedUKs: []*UniqueKey{
   618  				{Name: "uidx123", Columns: *columns123},
   619  			},
   620  			expectRemovedUKs: emptyUniqueKeys,
   621  		},
   622  		{
   623  			name: "no match for different column names, expand",
   624  			sourceUKs: []*UniqueKey{
   625  				{Name: "uidx1", Columns: *columns1},
   626  				{Name: "uidx12", Columns: *columns12},
   627  				{Name: "uidx123", Columns: *columns123},
   628  			},
   629  			targetUKs: []*UniqueKey{
   630  				{Name: "uidx21", Columns: *columns21},
   631  				{Name: "uidx12A", Columns: *columns12A},
   632  			},
   633  			renameMap: map[string]string{"c3": "cx"},
   634  			// 123 expands 12, so even though 3 is mapped to x, 123 is still not more constrained.
   635  			expectAddedUKs: emptyUniqueKeys,
   636  			expectRemovedUKs: []*UniqueKey{
   637  				{Name: "uidx1", Columns: *columns1},
   638  			},
   639  		},
   640  		{
   641  			name: "no match for different column names, no expand",
   642  			sourceUKs: []*UniqueKey{
   643  				{Name: "uidx123", Columns: *columns123},
   644  			},
   645  			targetUKs: []*UniqueKey{
   646  				{Name: "uidx12A", Columns: *columns12A},
   647  			},
   648  			renameMap: map[string]string{"c3": "cx"},
   649  			expectAddedUKs: []*UniqueKey{
   650  				{Name: "uidx12A", Columns: *columns12A},
   651  			},
   652  			expectRemovedUKs: []*UniqueKey{
   653  				{Name: "uidx123", Columns: *columns123},
   654  			},
   655  		},
   656  	}
   657  
   658  	for _, tc := range tt {
   659  		t.Run(tc.name, func(t *testing.T) {
   660  			addedUKs := AddedUniqueKeys(tc.sourceUKs, tc.targetUKs, tc.renameMap)
   661  			assert.Equal(t, tc.expectAddedUKs, addedUKs)
   662  			removedUKs := RemovedUniqueKeys(tc.sourceUKs, tc.targetUKs, tc.renameMap)
   663  			assert.Equal(t, tc.expectRemovedUKs, removedUKs)
   664  		})
   665  	}
   666  }