207 lines
5.4 KiB
Go
207 lines
5.4 KiB
Go
// Copyright 2019 Dolthub, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package diff
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
|
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
|
|
)
|
|
|
|
type SchemaChangeType int
|
|
|
|
const (
|
|
// SchDiffNone is the SchemaChangeType for two columns with the same tag that are identical
|
|
SchDiffNone SchemaChangeType = iota
|
|
// SchDiffAdded is the SchemaChangeType when a column is in the new schema but not the old
|
|
SchDiffAdded
|
|
// SchDiffRemoved is the SchemaChangeType when a column is in the old schema but not the new
|
|
SchDiffRemoved
|
|
// SchDiffModified is the SchemaChangeType for two columns with the same tag that are different
|
|
SchDiffModified
|
|
)
|
|
|
|
// ColumnDifference is the result of comparing two columns from two schemas.
|
|
type ColumnDifference struct {
|
|
DiffType SchemaChangeType
|
|
Tag uint64
|
|
Old *schema.Column
|
|
New *schema.Column
|
|
}
|
|
|
|
type columnPair [2]*schema.Column
|
|
|
|
// DiffSchColumns compares two schemas by looking at columns with the same tag.
|
|
func DiffSchColumns(fromSch, toSch schema.Schema) (map[uint64]ColumnDifference, []uint64) {
|
|
colPairMap, unionTags := pairColumns(fromSch, toSch)
|
|
|
|
diffs := make(map[uint64]ColumnDifference)
|
|
for _, tag := range unionTags {
|
|
colPair := colPairMap[tag]
|
|
if colPair[0] == nil {
|
|
diffs[tag] = ColumnDifference{SchDiffAdded, tag, nil, colPair[1]}
|
|
} else if colPair[1] == nil {
|
|
diffs[tag] = ColumnDifference{SchDiffRemoved, tag, colPair[0], nil}
|
|
} else if !colPair[0].Equals(*colPair[1]) {
|
|
diffs[tag] = ColumnDifference{SchDiffModified, tag, colPair[0], colPair[1]}
|
|
} else {
|
|
diffs[tag] = ColumnDifference{SchDiffNone, tag, colPair[0], colPair[1]}
|
|
}
|
|
}
|
|
|
|
return diffs, unionTags
|
|
}
|
|
|
|
// pairColumns loops over both sets of columns pairing columns with the same tag.
|
|
func pairColumns(fromSch, toSch schema.Schema) (map[uint64]columnPair, []uint64) {
|
|
// collect the tag union of the two schemas, ordering fromSch before toSch
|
|
var unionTags []uint64
|
|
colPairMap := make(map[uint64]columnPair)
|
|
|
|
_ = fromSch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) {
|
|
colPairMap[tag] = columnPair{&col, nil}
|
|
unionTags = append(unionTags, tag)
|
|
|
|
return false, nil
|
|
})
|
|
|
|
_ = toSch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) {
|
|
if pair, ok := colPairMap[tag]; ok {
|
|
pair[1] = &col
|
|
colPairMap[tag] = pair
|
|
} else {
|
|
colPairMap[tag] = columnPair{nil, &col}
|
|
unionTags = append(unionTags, tag)
|
|
}
|
|
|
|
return false, nil
|
|
})
|
|
|
|
return colPairMap, unionTags
|
|
}
|
|
|
|
type IndexDifference struct {
|
|
DiffType SchemaChangeType
|
|
From schema.Index
|
|
To schema.Index
|
|
}
|
|
|
|
// DiffSchIndexes matches two sets of Indexes based on column definitions.
|
|
// It returns matched and unmatched Indexes as a slice of IndexDifferences.
|
|
func DiffSchIndexes(fromSch, toSch schema.Schema) (diffs []IndexDifference) {
|
|
_ = fromSch.Indexes().Iter(func(fromIdx schema.Index) (stop bool, err error) {
|
|
toIdx, ok := toSch.Indexes().GetIndexByTags(fromIdx.IndexedColumnTags()...)
|
|
|
|
if !ok {
|
|
diffs = append(diffs, IndexDifference{
|
|
DiffType: SchDiffRemoved,
|
|
From: fromIdx,
|
|
})
|
|
return false, nil
|
|
}
|
|
|
|
d := IndexDifference{
|
|
DiffType: SchDiffModified,
|
|
From: fromIdx,
|
|
To: toIdx,
|
|
}
|
|
|
|
if fromIdx.Equals(toIdx) {
|
|
d.DiffType = SchDiffNone
|
|
}
|
|
diffs = append(diffs, d)
|
|
|
|
return false, nil
|
|
})
|
|
|
|
_ = toSch.Indexes().Iter(func(toIdx schema.Index) (stop bool, err error) {
|
|
// if we've seen this index, skip
|
|
for _, d := range diffs {
|
|
if d.To != nil && d.To.Equals(toIdx) {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
diffs = append(diffs, IndexDifference{
|
|
DiffType: SchDiffAdded,
|
|
To: toIdx,
|
|
})
|
|
|
|
return false, nil
|
|
})
|
|
|
|
return diffs
|
|
}
|
|
|
|
type ForeignKeyDifference struct {
|
|
DiffType SchemaChangeType
|
|
From doltdb.ForeignKey
|
|
To doltdb.ForeignKey
|
|
}
|
|
|
|
// DiffForeignKeys matches two sets of ForeignKeys based on column definitions.
|
|
// It returns matched and unmatched ForeignKeys as a slice of ForeignKeyDifferences.
|
|
func DiffForeignKeys(fromFks, toFKs []doltdb.ForeignKey) (diffs []ForeignKeyDifference) {
|
|
for _, from := range fromFks {
|
|
matched := false
|
|
for _, to := range toFKs {
|
|
if reflect.DeepEqual(from.ReferencedTableColumns, to.ReferencedTableColumns) &&
|
|
reflect.DeepEqual(from.TableColumns, to.TableColumns) {
|
|
|
|
matched = true
|
|
d := ForeignKeyDifference{
|
|
DiffType: SchDiffModified,
|
|
From: from,
|
|
To: to,
|
|
}
|
|
|
|
if from.DeepEquals(to) {
|
|
d.DiffType = SchDiffNone
|
|
}
|
|
diffs = append(diffs, d)
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if !matched {
|
|
diffs = append(diffs, ForeignKeyDifference{
|
|
DiffType: SchDiffRemoved,
|
|
From: from,
|
|
})
|
|
}
|
|
}
|
|
|
|
for _, to := range toFKs {
|
|
seen := false
|
|
for _, d := range diffs {
|
|
if d.To.DeepEquals(to) {
|
|
seen = true
|
|
break
|
|
}
|
|
}
|
|
if seen {
|
|
continue
|
|
}
|
|
|
|
diffs = append(diffs, ForeignKeyDifference{
|
|
DiffType: SchDiffAdded,
|
|
To: to,
|
|
})
|
|
}
|
|
return diffs
|
|
}
|