github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/merge/conflict_source.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 merge 16 17 import ( 18 "context" 19 "errors" 20 "io" 21 22 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 23 "github.com/dolthub/dolt/go/libraries/doltcore/row" 24 "github.com/dolthub/dolt/go/libraries/doltcore/rowconv" 25 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 26 "github.com/dolthub/dolt/go/libraries/doltcore/table/pipeline" 27 "github.com/dolthub/dolt/go/store/types" 28 ) 29 30 const ( 31 mergeVersionProp = "merge_version" 32 mergeRowOperation = "row_operation" 33 34 oursStr = "our" 35 theirsStr = "their" 36 baseStr = "base" 37 ) 38 39 // ConflictReader is a class providing a NextConflict function which can be used in a pipeline as a pipeline.SourceFunc, 40 // or it can be used to read each conflict 41 type ConflictReader struct { 42 confItr types.MapIterator 43 joiner *rowconv.Joiner 44 nbf *types.NomsBinFormat 45 } 46 47 // NewConflictReader returns a new conflict reader for a given table 48 func NewConflictReader(ctx context.Context, tbl *doltdb.Table) (*ConflictReader, error) { 49 base, sch, mergeSch, err := tbl.GetConflictSchemas(ctx) 50 51 if err != nil { 52 return nil, err 53 } 54 55 joiner, err := rowconv.NewJoiner( 56 []rowconv.NamedSchema{ 57 {Name: baseStr, Sch: base}, 58 {Name: oursStr, Sch: sch}, 59 {Name: theirsStr, Sch: mergeSch}, 60 }, map[string]rowconv.ColNamingFunc{ 61 baseStr: func(colName string) string { return baseStr + "_" + colName }, 62 oursStr: func(colName string) string { return oursStr + "_" + colName }, 63 theirsStr: func(colName string) string { return theirsStr + "_" + colName }, 64 }, 65 ) 66 67 if err != nil { 68 return nil, err 69 } 70 71 _, confData, err := tbl.GetConflicts(ctx) 72 73 if err != nil { 74 return nil, err 75 } 76 77 confItr, err := confData.Iterator(ctx) 78 79 if err != nil { 80 return nil, err 81 } 82 83 return &ConflictReader{confItr, joiner, tbl.Format()}, nil 84 } 85 86 func tagMappingConverter(ctx context.Context, vrw types.ValueReadWriter, src, dest schema.Schema) (*rowconv.RowConverter, error) { 87 mapping, err := rowconv.TagMapping(src, dest) 88 89 if err != nil { 90 return nil, err 91 } 92 93 return rowconv.NewRowConverter(ctx, vrw, mapping) 94 } 95 96 // GetSchema gets the schema of the rows that this reader will return 97 func (cr *ConflictReader) GetSchema() schema.Schema { 98 return cr.joiner.GetSchema() 99 } 100 101 // GetJoiner returns the joiner used to join a row with its base, and merge versions 102 func (cr *ConflictReader) GetJoiner() *rowconv.Joiner { 103 return cr.joiner 104 } 105 106 // NextConflict can be called successively to retrieve the conflicts in a table. Once all conflicts have been returned 107 // io.EOF will be returned in the error field. This can be used in a pipeline, or to iterate through all the conflicts 108 // in a table. 109 func (cr *ConflictReader) NextConflict(ctx context.Context) (row.Row, pipeline.ImmutableProperties, error) { 110 key, value, err := cr.confItr.Next(ctx) 111 112 if err != nil { 113 return nil, pipeline.NoProps, err 114 } 115 116 if key == nil { 117 return nil, pipeline.NoProps, io.EOF 118 } 119 120 keyTpl := key.(types.Tuple) 121 conflict, err := doltdb.ConflictFromTuple(value.(types.Tuple)) 122 123 if err != nil { 124 return nil, pipeline.NoProps, err 125 } 126 127 namedRows := make(map[string]row.Row) 128 if !types.IsNull(conflict.Base) { 129 namedRows[baseStr], err = row.FromNoms(cr.joiner.SchemaForName(baseStr), keyTpl, conflict.Base.(types.Tuple)) 130 131 if err != nil { 132 return nil, pipeline.NoProps, err 133 } 134 } 135 136 if !types.IsNull(conflict.Value) { 137 namedRows[oursStr], err = row.FromNoms(cr.joiner.SchemaForName(oursStr), keyTpl, conflict.Value.(types.Tuple)) 138 139 if err != nil { 140 return nil, pipeline.NoProps, err 141 } 142 } 143 144 if !types.IsNull(conflict.MergeValue) { 145 namedRows[theirsStr], err = row.FromNoms(cr.joiner.SchemaForName(theirsStr), keyTpl, conflict.MergeValue.(types.Tuple)) 146 147 if err != nil { 148 return nil, pipeline.NoProps, err 149 } 150 } 151 152 joinedRow, err := cr.joiner.Join(namedRows) 153 154 if err != nil { 155 return nil, pipeline.NoProps, err 156 } 157 158 return joinedRow, pipeline.NoProps, nil 159 } 160 161 // GetKeyForConflicts returns the pk for a conflict row 162 func (cr *ConflictReader) GetKeyForConflict(ctx context.Context, r row.Row) (types.Value, error) { 163 rows, err := cr.joiner.Split(r) 164 165 if err != nil { 166 return nil, err 167 } 168 169 for rowType, r := range rows { 170 key, err := r.NomsMapKey(cr.joiner.SchemaForName(rowType)).Value(ctx) 171 172 if err != nil { 173 return nil, err 174 } 175 176 return key, nil 177 } 178 179 return nil, errors.New("could not determine key") 180 } 181 182 // Close should release resources being held 183 func (cr *ConflictReader) Close() error { 184 return nil 185 }