github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/diff/schema_diff.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 diff 16 17 import ( 18 "reflect" 19 20 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 21 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 22 ) 23 24 type SchemaChangeType int 25 26 const ( 27 // SchDiffNone is the SchemaChangeType for two columns with the same tag that are identical 28 SchDiffNone SchemaChangeType = iota 29 // SchDiffAdded is the SchemaChangeType when a column is in the new schema but not the old 30 SchDiffAdded 31 // SchDiffRemoved is the SchemaChangeType when a column is in the old schema but not the new 32 SchDiffRemoved 33 // SchDiffModified is the SchemaChangeType for two columns with the same tag that are different 34 SchDiffModified 35 ) 36 37 // ColumnDifference is the result of comparing two columns from two schemas. 38 type ColumnDifference struct { 39 DiffType SchemaChangeType 40 Tag uint64 41 Old *schema.Column 42 New *schema.Column 43 } 44 45 type columnPair [2]*schema.Column 46 47 // DiffSchColumns compares two schemas by looking at columns with the same tag. 48 func DiffSchColumns(fromSch, toSch schema.Schema) (map[uint64]ColumnDifference, []uint64) { 49 colPairMap, unionTags := pairColumns(fromSch, toSch) 50 51 diffs := make(map[uint64]ColumnDifference) 52 for _, tag := range unionTags { 53 colPair := colPairMap[tag] 54 if colPair[0] == nil { 55 diffs[tag] = ColumnDifference{SchDiffAdded, tag, nil, colPair[1]} 56 } else if colPair[1] == nil { 57 diffs[tag] = ColumnDifference{SchDiffRemoved, tag, colPair[0], nil} 58 } else if !colPair[0].Equals(*colPair[1]) { 59 diffs[tag] = ColumnDifference{SchDiffModified, tag, colPair[0], colPair[1]} 60 } else { 61 diffs[tag] = ColumnDifference{SchDiffNone, tag, colPair[0], colPair[1]} 62 } 63 } 64 65 return diffs, unionTags 66 } 67 68 // pairColumns loops over both sets of columns pairing columns with the same tag. 69 func pairColumns(fromSch, toSch schema.Schema) (map[uint64]columnPair, []uint64) { 70 // collect the tag union of the two schemas, ordering fromSch before toSch 71 var unionTags []uint64 72 colPairMap := make(map[uint64]columnPair) 73 74 _ = fromSch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 75 colPairMap[tag] = columnPair{&col, nil} 76 unionTags = append(unionTags, tag) 77 78 return false, nil 79 }) 80 81 _ = toSch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 82 if pair, ok := colPairMap[tag]; ok { 83 pair[1] = &col 84 colPairMap[tag] = pair 85 } else { 86 colPairMap[tag] = columnPair{nil, &col} 87 unionTags = append(unionTags, tag) 88 } 89 90 return false, nil 91 }) 92 93 return colPairMap, unionTags 94 } 95 96 type IndexDifference struct { 97 DiffType SchemaChangeType 98 From schema.Index 99 To schema.Index 100 } 101 102 // DiffSchIndexes matches two sets of Indexes based on column definitions. 103 // It returns matched and unmatched Indexes as a slice of IndexDifferences. 104 func DiffSchIndexes(fromSch, toSch schema.Schema) (diffs []IndexDifference) { 105 _ = fromSch.Indexes().Iter(func(fromIdx schema.Index) (stop bool, err error) { 106 toIdx, ok := toSch.Indexes().GetIndexByTags(fromIdx.IndexedColumnTags()...) 107 108 if !ok { 109 diffs = append(diffs, IndexDifference{ 110 DiffType: SchDiffRemoved, 111 From: fromIdx, 112 }) 113 return false, nil 114 } 115 116 d := IndexDifference{ 117 DiffType: SchDiffModified, 118 From: fromIdx, 119 To: toIdx, 120 } 121 122 if fromIdx.Equals(toIdx) { 123 d.DiffType = SchDiffNone 124 } 125 diffs = append(diffs, d) 126 127 return false, nil 128 }) 129 130 _ = toSch.Indexes().Iter(func(toIdx schema.Index) (stop bool, err error) { 131 // if we've seen this index, skip 132 for _, d := range diffs { 133 if d.To != nil && d.To.Equals(toIdx) { 134 return false, nil 135 } 136 } 137 138 diffs = append(diffs, IndexDifference{ 139 DiffType: SchDiffAdded, 140 To: toIdx, 141 }) 142 143 return false, nil 144 }) 145 146 return diffs 147 } 148 149 type ForeignKeyDifference struct { 150 DiffType SchemaChangeType 151 From doltdb.ForeignKey 152 To doltdb.ForeignKey 153 } 154 155 // DiffForeignKeys matches two sets of ForeignKeys based on column definitions. 156 // It returns matched and unmatched ForeignKeys as a slice of ForeignKeyDifferences. 157 func DiffForeignKeys(fromFks, toFKs []doltdb.ForeignKey) (diffs []ForeignKeyDifference) { 158 for _, from := range fromFks { 159 matched := false 160 for _, to := range toFKs { 161 if reflect.DeepEqual(from.ReferencedTableColumns, to.ReferencedTableColumns) && 162 reflect.DeepEqual(from.TableColumns, to.TableColumns) { 163 164 matched = true 165 d := ForeignKeyDifference{ 166 DiffType: SchDiffModified, 167 From: from, 168 To: to, 169 } 170 171 if from.DeepEquals(to) { 172 d.DiffType = SchDiffNone 173 } 174 diffs = append(diffs, d) 175 176 break 177 } 178 } 179 180 if !matched { 181 diffs = append(diffs, ForeignKeyDifference{ 182 DiffType: SchDiffRemoved, 183 From: from, 184 }) 185 } 186 } 187 188 for _, to := range toFKs { 189 seen := false 190 for _, d := range diffs { 191 if d.To.DeepEquals(to) { 192 seen = true 193 break 194 } 195 } 196 if seen { 197 continue 198 } 199 200 diffs = append(diffs, ForeignKeyDifference{ 201 DiffType: SchDiffAdded, 202 To: to, 203 }) 204 } 205 return diffs 206 }