vitess.io/vitess@v0.16.2/go/vt/vttablet/onlineddl/vrepl/unique_key.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  	"strings"
    25  )
    26  
    27  // UniqueKeyValidForIteration returns 'false' if we should not use this unique key as the main
    28  // iteration key in vreplication.
    29  func UniqueKeyValidForIteration(uniqueKey *UniqueKey) bool {
    30  	if uniqueKey.HasNullable {
    31  		// NULLable columns in a unique key means the set of values is not really unique (two identical rows with NULLs are allowed).
    32  		// Thus, we cannot use this unique key for iteration.
    33  		return false
    34  	}
    35  	if uniqueKey.HasFloat {
    36  		// float & double data types are imprecise and we cannot use them while iterating unique keys
    37  		return false
    38  	}
    39  	return true // good to go!
    40  }
    41  
    42  // GetSharedUniqueKeys returns the unique keys shared between the source & target tables
    43  func GetSharedUniqueKeys(sourceUniqueKeys, targetUniqueKeys [](*UniqueKey), columnRenameMap map[string]string) (chosenSourceUniqueKey, chosenTargetUniqueKey *UniqueKey) {
    44  	type ukPair struct{ source, target *UniqueKey }
    45  	var sharedUKPairs []*ukPair
    46  
    47  	for _, sourceUniqueKey := range sourceUniqueKeys {
    48  		if !UniqueKeyValidForIteration(sourceUniqueKey) {
    49  			continue
    50  		}
    51  		for _, targetUniqueKey := range targetUniqueKeys {
    52  			if !UniqueKeyValidForIteration(targetUniqueKey) {
    53  				continue
    54  			}
    55  			uniqueKeyMatches := func() bool {
    56  				// Compare two unique keys
    57  				if sourceUniqueKey.Columns.Len() != targetUniqueKey.Columns.Len() {
    58  					return false
    59  				}
    60  				// Expect same columns, same order, potentially column name mapping
    61  				sourceUniqueKeyNames := sourceUniqueKey.Columns.Names()
    62  				targetUniqueKeyNames := targetUniqueKey.Columns.Names()
    63  				for i := range sourceUniqueKeyNames {
    64  					sourceColumnName := sourceUniqueKeyNames[i]
    65  					targetColumnName := targetUniqueKeyNames[i]
    66  					mappedSourceColumnName := sourceColumnName
    67  					if mapped, ok := columnRenameMap[sourceColumnName]; ok {
    68  						mappedSourceColumnName = mapped
    69  					}
    70  					if !strings.EqualFold(mappedSourceColumnName, targetColumnName) {
    71  						return false
    72  					}
    73  				}
    74  				return true
    75  			}
    76  			if uniqueKeyMatches() {
    77  				sharedUKPairs = append(sharedUKPairs, &ukPair{source: sourceUniqueKey, target: targetUniqueKey})
    78  			}
    79  		}
    80  	}
    81  	// Now that we know what the shared unique keys are, let's find the "best" shared one.
    82  	// Source and target unique keys can have different name, even though they cover the exact same
    83  	// columns and in same order.
    84  	for _, pair := range sharedUKPairs {
    85  		if pair.source.HasNullable {
    86  			continue
    87  		}
    88  		if pair.target.HasNullable {
    89  			continue
    90  		}
    91  		return pair.source, pair.target
    92  	}
    93  	return nil, nil
    94  }
    95  
    96  // SourceUniqueKeyAsOrMoreConstrainedThanTarget returns 'true' when sourceUniqueKey is at least as constrained as targetUniqueKey.
    97  // "More constrained" means the uniqueness constraint is "stronger". Thus, if sourceUniqueKey is as-or-more constrained than targetUniqueKey, then
    98  // rows valid under sourceUniqueKey must also be valid in targetUniqueKey. The opposite is not necessarily so: rows that are valid in targetUniqueKey
    99  // may cause a unique key violation under sourceUniqueKey
   100  func SourceUniqueKeyAsOrMoreConstrainedThanTarget(sourceUniqueKey, targetUniqueKey *UniqueKey, columnRenameMap map[string]string) bool {
   101  	// Compare two unique keys
   102  	if sourceUniqueKey.Columns.Len() > targetUniqueKey.Columns.Len() {
   103  		// source can't be more constrained if it covers *more* columns
   104  		return false
   105  	}
   106  	// we know that len(sourceUniqueKeyNames) <= len(targetUniqueKeyNames)
   107  	sourceUniqueKeyNames := sourceUniqueKey.Columns.Names()
   108  	targetUniqueKeyNames := targetUniqueKey.Columns.Names()
   109  	// source is more constrained than target if every column in source is also in target, order is immaterial
   110  	for i := range sourceUniqueKeyNames {
   111  		sourceColumnName := sourceUniqueKeyNames[i]
   112  		mappedSourceColumnName := sourceColumnName
   113  		if mapped, ok := columnRenameMap[sourceColumnName]; ok {
   114  			mappedSourceColumnName = mapped
   115  		}
   116  		columnFoundInTarget := func() bool {
   117  			for _, targetColumnName := range targetUniqueKeyNames {
   118  				if strings.EqualFold(mappedSourceColumnName, targetColumnName) {
   119  					return true
   120  				}
   121  			}
   122  			return false
   123  		}
   124  		if !columnFoundInTarget() {
   125  			return false
   126  		}
   127  	}
   128  	return true
   129  }
   130  
   131  // AddedUniqueKeys returns the unique key constraints added in target. This does not necessarily mean that the unique key itself is new,
   132  // rather that there's a new, stricter constraint on a set of columns, that didn't exist before. Example:
   133  //
   134  //	before: unique key `my_key`(c1, c2, c3); after: unique key `my_key`(c1, c2)
   135  //	The constraint on (c1, c2) is new; and `my_key` in target table ("after") is considered a new key
   136  //
   137  // Order of columns is immaterial to uniqueness of column combination.
   138  func AddedUniqueKeys(sourceUniqueKeys, targetUniqueKeys [](*UniqueKey), columnRenameMap map[string]string) (addedUKs [](*UniqueKey)) {
   139  	addedUKs = [](*UniqueKey){}
   140  	for _, targetUniqueKey := range targetUniqueKeys {
   141  		foundAsOrMoreConstrainingSourceKey := func() bool {
   142  			for _, sourceUniqueKey := range sourceUniqueKeys {
   143  				if SourceUniqueKeyAsOrMoreConstrainedThanTarget(sourceUniqueKey, targetUniqueKey, columnRenameMap) {
   144  					// target key does not add a new constraint
   145  					return true
   146  				}
   147  			}
   148  			return false
   149  		}
   150  		if !foundAsOrMoreConstrainingSourceKey() {
   151  			addedUKs = append(addedUKs, targetUniqueKey)
   152  		}
   153  	}
   154  	return addedUKs
   155  }
   156  
   157  // RemovedUniqueKeys returns the list of unique key constraints _removed_ going from source to target.
   158  func RemovedUniqueKeys(sourceUniqueKeys, targetUniqueKeys [](*UniqueKey), columnRenameMap map[string]string) (removedUKs [](*UniqueKey)) {
   159  	reverseColumnRenameMap := map[string]string{}
   160  	for k, v := range columnRenameMap {
   161  		reverseColumnRenameMap[v] = k
   162  	}
   163  	return AddedUniqueKeys(targetUniqueKeys, sourceUniqueKeys, reverseColumnRenameMap)
   164  }
   165  
   166  // GetUniqueKeyCoveredByColumns returns the first unique key from given list, whose columns all appear
   167  // in given column list.
   168  func GetUniqueKeyCoveredByColumns(uniqueKeys [](*UniqueKey), columns *ColumnList) (chosenUniqueKey *UniqueKey) {
   169  	for _, uniqueKey := range uniqueKeys {
   170  		if !UniqueKeyValidForIteration(uniqueKey) {
   171  			continue
   172  		}
   173  		if uniqueKey.Columns.IsSubsetOf(columns) {
   174  			return uniqueKey
   175  		}
   176  	}
   177  	return nil
   178  }