github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/diff/diff_splitter.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 "github.com/dolthub/dolt/go/libraries/doltcore/row" 19 "github.com/dolthub/dolt/go/libraries/doltcore/rowconv" 20 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 21 "github.com/dolthub/dolt/go/libraries/doltcore/table/pipeline" 22 "github.com/dolthub/dolt/go/libraries/utils/valutil" 23 ) 24 25 const ( 26 // DiffTypeProp is the name of a property added to each split row which tells if its added, removed, the modified 27 // old value, or the new value after modification 28 DiffTypeProp = "difftype" 29 30 // CollChangesProp is the name of a property added to each modified row which is a map from collumn name to the 31 // type of change. 32 CollChangesProp = "collchanges" 33 ) 34 35 // DiffChType is an enum that represents the type of change 36 type DiffChType int 37 38 const ( 39 // DiffAdded is the DiffTypeProp value for a row that was newly added (In new, but not in old) 40 DiffAdded DiffChType = iota 41 42 // DiffRemoved is the DiffTypeProp value for a row that was newly deleted (In old, but not in new) 43 DiffRemoved 44 45 // DiffModifiedOld is the DiffTypeProp value for the row which represents the old value of the row before it was changed. 46 DiffModifiedOld 47 48 // DiffModifiedNew is the DiffTypeProp value for the row which represents the new value of the row after it was changed. 49 DiffModifiedNew 50 ) 51 52 // DiffTyped is an interface for an object that has a DiffChType 53 type DiffTyped interface { 54 // DiffType gets the DiffChType of an object 55 DiffType() DiffChType 56 } 57 58 // DiffRow is a row.Row with a change type associated with it. 59 type DiffRow struct { 60 row.Row 61 diffType DiffChType 62 } 63 64 // DiffType gets the DiffChType for the row. 65 func (dr *DiffRow) DiffType() DiffChType { 66 return dr.diffType 67 } 68 69 // DiffSplitter is a struct that can take a diff which is represented by a row with a column for every field in the old 70 // version, and a column for every field in the new version and split it into two rows with properties which annotate 71 // what each row is. This is used to show diffs as 2 lines, instead of 1. 72 type DiffSplitter struct { 73 joiner *rowconv.Joiner 74 oldConv *rowconv.RowConverter 75 newConv *rowconv.RowConverter 76 } 77 78 // NewDiffSplitter creates a DiffSplitter 79 func NewDiffSplitter(joiner *rowconv.Joiner, oldConv, newConv *rowconv.RowConverter) *DiffSplitter { 80 return &DiffSplitter{joiner, oldConv, newConv} 81 } 82 83 func convertNamedRow(rows map[string]row.Row, name string, rc *rowconv.RowConverter) (row.Row, error) { 84 r, ok := rows[name] 85 86 if !ok || r == nil { 87 return nil, nil 88 } 89 90 return rc.Convert(r) 91 } 92 93 // SplitDiffIntoOldAndNew is a pipeline.TransformRowFunc which can be used in a pipeline to split single row diffs, 94 // into 2 row diffs. 95 func (ds *DiffSplitter) SplitDiffIntoOldAndNew(inRow row.Row, props pipeline.ReadableMap) (rowData []*pipeline.TransformedRowResult, badRowDetails string) { 96 rows, err := ds.joiner.Split(inRow) 97 if err != nil { 98 return nil, err.Error() 99 } 100 101 mappedOld, err := convertNamedRow(rows, From, ds.oldConv) 102 103 if err != nil { 104 return nil, err.Error() 105 } 106 107 mappedNew, err := convertNamedRow(rows, To, ds.newConv) 108 109 if err != nil { 110 return nil, err.Error() 111 } 112 113 originalNewSch := ds.joiner.SchemaForName(From) 114 originalOldSch := ds.joiner.SchemaForName(To) 115 116 var oldProps = map[string]interface{}{DiffTypeProp: DiffRemoved} 117 var newProps = map[string]interface{}{DiffTypeProp: DiffAdded} 118 if mappedOld != nil && mappedNew != nil { 119 oldColDiffs := make(map[string]DiffChType) 120 newColDiffs := make(map[string]DiffChType) 121 122 outSch := ds.newConv.DestSch 123 outCols := outSch.GetAllCols() 124 err := outCols.Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 125 oldVal, _ := mappedOld.GetColVal(tag) 126 newVal, _ := mappedNew.GetColVal(tag) 127 128 _, inOld := originalOldSch.GetAllCols().GetByTag(tag) 129 _, inNew := originalNewSch.GetAllCols().GetByTag(tag) 130 131 if inOld && inNew { 132 if !valutil.NilSafeEqCheck(oldVal, newVal) { 133 newColDiffs[col.Name] = DiffModifiedNew 134 oldColDiffs[col.Name] = DiffModifiedOld 135 } 136 } else if inOld { 137 oldColDiffs[col.Name] = DiffRemoved 138 } else { 139 newColDiffs[col.Name] = DiffAdded 140 } 141 142 return false, nil 143 }) 144 145 if err != nil { 146 return nil, err.Error() 147 } 148 149 oldProps = map[string]interface{}{DiffTypeProp: DiffModifiedOld, CollChangesProp: oldColDiffs} 150 newProps = map[string]interface{}{DiffTypeProp: DiffModifiedNew, CollChangesProp: newColDiffs} 151 } 152 153 var results []*pipeline.TransformedRowResult 154 if mappedOld != nil { 155 results = append(results, &pipeline.TransformedRowResult{RowData: mappedOld, PropertyUpdates: oldProps}) 156 } 157 158 if mappedNew != nil { 159 results = append(results, &pipeline.TransformedRowResult{RowData: mappedNew, PropertyUpdates: newProps}) 160 } 161 162 return results, "" 163 }