github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/diff/apply_patch.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 // This file incorporates work covered by the following copyright and 16 // permission notice: 17 // 18 // Copyright 2016 Attic Labs, Inc. All rights reserved. 19 // Licensed under the Apache License, version 2.0: 20 // http://www.apache.org/licenses/LICENSE-2.0 21 22 package diff 23 24 import ( 25 "context" 26 "fmt" 27 28 "github.com/dolthub/dolt/go/store/d" 29 "github.com/dolthub/dolt/go/store/types" 30 ) 31 32 // Apply applies a Patch (list of diffs) to a graph. It fulfills the 33 // following contract: 34 // Given 2 Noms graphs: a1 and a2: 35 // ApplyPatch(a1, Diff(a1, a2)) == a2 36 // This is useful for IncrementalUpdate() and possibly other problems. See 37 // updater.go for more information. 38 // 39 // This function uses a patchStack to maintain state of the graph as it cycles 40 // through the diffs in a patch, applying them to 'root' one by one. Because the 41 // Difference objects in the patch can be sorted according to their path, each 42 // one is applied in order. When done in combination with the stack, this enables 43 // all Differences that change a particular node to be applied to that node 44 // before it gets assigned back to it's parent. 45 func Apply(ctx context.Context, nbf *types.NomsBinFormat, root types.Value, patch Patch) (types.Value, error) { 46 if len(patch) == 0 { 47 return root, nil 48 } 49 50 var lastPath types.Path 51 stack := patchStack{} 52 types.SortWithErroringLess(PatchSort{patch, nbf}) 53 54 // Push the element on the stack that corresponds to the root 55 // node. 56 stack.push(nil, nil, types.DiffChangeModified, root, nil, nil) 57 58 for _, dif := range patch { 59 // get the path where this dif needs to be applied 60 p := dif.Path 61 62 // idx will hold the index of the last common element between p and 63 // lastPath (p from the last iteration). 64 var idx int 65 66 // p can be identical to lastPath in certain cases. For example, when 67 // one item gets removed from a list at the same place another item 68 // is added to it. In this case, we need pop the last operation of the 69 // stack early and set the idx to be the len(p) - 1. 70 // Otherwise, if the paths are different we can call commonPrefixCount() 71 if len(p) > 0 && p.Equals(lastPath) { 72 _, err := stack.pop(ctx) 73 74 if err != nil { 75 return nil, err 76 } 77 78 idx = len(p) - 1 79 } else { 80 idx = commonPrefixCount(lastPath, p) 81 } 82 lastPath = p 83 84 // if the stack has elements on it leftover from the last iteration. Pop 85 // those elements until the stack only has values in it that are 86 // referenced by this p. Popping an element on the stack, folds that 87 // value into it's parent. 88 for idx < stack.Len()-1 { 89 _, err := stack.pop(ctx) 90 91 if err != nil { 92 return nil, err 93 } 94 } 95 96 // tail is the part of the current path that has not yet been pushed 97 // onto the stack. Iterate over those pathParts and push those values 98 // onto the stack. 99 tail := p[idx:] 100 for i, pp := range tail { 101 top := stack.top() 102 parent := top.newestValue() 103 oldValue, err := pp.Resolve(ctx, parent, nil) 104 105 if err != nil { 106 return nil, err 107 } 108 109 var newValue types.Value 110 if i == len(tail)-1 { // last pathPart in this path 111 newValue = oldValue 112 oldValue = dif.OldValue 113 } 114 // Any intermediate elements on the stack will have a changeType 115 // of modified. Leaf elements will be updated below to reflect the 116 // actual changeType. 117 stack.push(p, pp, types.DiffChangeModified, oldValue, newValue, dif.NewKeyValue) 118 } 119 120 // Update the top element in the stack with changeType from the dif and 121 // the NewValue from the diff 122 se := stack.top() 123 se.newValue = dif.NewValue 124 se.changeType = dif.ChangeType 125 } 126 127 // We're done applying diffs to the graph. Pop any elements left on the 128 // stack and return the new root. 129 var newRoot stackElem 130 for stack.Len() > 0 { 131 var err error 132 newRoot, err = stack.pop(ctx) 133 134 if err != nil { 135 return nil, err 136 } 137 } 138 return newRoot.newValue, nil 139 } 140 141 // updateNode handles the actual update of a node. It uses 'pp' to get the 142 // information that it needs to update 'parent' with 'newVal'. 'oldVal' is also 143 // passed in so that Sets can be updated correctly. This function is used by 144 // the patchStack Pop() function to merge values into a new graph. 145 func (stack *patchStack) updateNode(ctx context.Context, top *stackElem, parent types.Value) (types.Value, error) { 146 d.PanicIfTrue(parent == nil) 147 switch part := top.pathPart.(type) { 148 case types.FieldPath: 149 switch top.changeType { 150 case types.DiffChangeAdded: 151 return parent.(types.Struct).Set(part.Name, top.newValue) 152 case types.DiffChangeRemoved: 153 return parent.(types.Struct).Delete(part.Name) 154 case types.DiffChangeModified: 155 return parent.(types.Struct).Set(part.Name, top.newValue) 156 } 157 case types.IndexPath: 158 switch el := parent.(type) { 159 case types.List: 160 idx := uint64(part.Index.(types.Float)) 161 offset := stack.adjustIndexOffset(top.path, top.changeType) 162 realIdx := idx + uint64(offset) 163 var nv types.Value 164 switch top.changeType { 165 case types.DiffChangeAdded: 166 if realIdx > el.Len() { 167 return el.Edit().Append(top.newValue).List(ctx) 168 } else { 169 return el.Edit().Insert(realIdx, top.newValue).List(ctx) 170 } 171 172 case types.DiffChangeRemoved: 173 return el.Edit().RemoveAt(realIdx).List(ctx) 174 case types.DiffChangeModified: 175 return el.Edit().Set(realIdx, top.newValue).List(ctx) 176 } 177 return nv, nil 178 case types.Map: 179 switch top.changeType { 180 case types.DiffChangeAdded: 181 return el.Edit().Set(part.Index, top.newValue).Map(ctx) 182 case types.DiffChangeRemoved: 183 return el.Edit().Remove(part.Index).Map(ctx) 184 case types.DiffChangeModified: 185 if part.IntoKey { 186 newPart := types.IndexPath{Index: part.Index} 187 ov, err := newPart.Resolve(ctx, parent, nil) 188 189 if err != nil { 190 return nil, err 191 } 192 193 return el.Edit().Remove(part.Index).Set(top.newValue, ov).Map(ctx) 194 } 195 return el.Edit().Set(part.Index, top.newValue).Map(ctx) 196 } 197 case types.Set: 198 if top.oldValue != nil { 199 se, err := el.Edit().Remove(top.oldValue) 200 201 if err != nil { 202 return nil, err 203 } 204 205 el, err = se.Set(ctx) 206 207 if err != nil { 208 return nil, err 209 } 210 } 211 212 if top.newValue != nil { 213 se, err := el.Edit().Insert(top.newValue) 214 215 if err != nil { 216 return nil, err 217 } 218 219 el, err = se.Set(ctx) 220 221 if err != nil { 222 return nil, err 223 } 224 } 225 226 return el, nil 227 } 228 case types.HashIndexPath: 229 switch el := parent.(type) { 230 case types.Set: 231 switch top.changeType { 232 case types.DiffChangeAdded: 233 se, err := el.Edit().Insert(top.newValue) 234 235 if err != nil { 236 return nil, err 237 } 238 239 return se.Set(ctx) 240 case types.DiffChangeRemoved: 241 se, err := el.Edit().Remove(top.oldValue) 242 243 if err != nil { 244 return nil, err 245 } 246 247 return se.Set(ctx) 248 case types.DiffChangeModified: 249 se, err := el.Edit().Remove(top.oldValue) 250 251 if err != nil { 252 return nil, err 253 } 254 255 se, err = se.Insert(top.newValue) 256 257 if err != nil { 258 return nil, err 259 } 260 261 return se.Set(ctx) 262 } 263 case types.Map: 264 keyPart := types.HashIndexPath{Hash: part.Hash, IntoKey: true} 265 k, err := keyPart.Resolve(ctx, parent, nil) 266 267 if err != nil { 268 return nil, err 269 } 270 271 switch top.changeType { 272 case types.DiffChangeAdded: 273 k := top.newKeyValue 274 return el.Edit().Set(k, top.newValue).Map(ctx) 275 case types.DiffChangeRemoved: 276 return el.Edit().Remove(k).Map(ctx) 277 case types.DiffChangeModified: 278 if part.IntoKey { 279 v, found, err := el.MaybeGet(ctx, k) 280 281 if err != nil { 282 return nil, err 283 } 284 285 d.PanicIfFalse(found) 286 return el.Edit().Remove(k).Set(top.newValue, v).Map(ctx) 287 } 288 return el.Edit().Set(k, top.newValue).Map(ctx) 289 } 290 } 291 } 292 panic(fmt.Sprintf("unreachable, pp.(type): %T", top.pathPart)) 293 } 294 295 // Returns the count of the number of PathParts that two paths have in a common 296 // prefix. The paths '.field1' and '.field2' have a 0 length common prefix. 297 // Todo: move to types.Path? 298 func commonPrefixCount(p1, p2 types.Path) int { 299 cnt := 0 300 301 for i, pp1 := range p1 { 302 var pp2 types.PathPart 303 if i < len(p2) { 304 pp2 = p2[i] 305 } 306 if pp1 != pp2 { 307 return cnt 308 } 309 cnt += 1 310 } 311 return cnt 312 } 313 314 type stackElem struct { 315 path types.Path 316 pathPart types.PathPart // from parent Value to this Value 317 changeType types.DiffChangeType 318 oldValue types.Value // can be nil if newValue is not nil 319 newValue types.Value // can be nil if oldValue is not nil 320 newKeyValue types.Value 321 } 322 323 // newestValue returns newValue if not nil, otherwise oldValue. This is useful 324 // when merging. Elements on the stack were 'push'ed there with the oldValue. 325 // newValue may have been set when a value was 'pop'ed above it. This method 326 // returns the last value that has been set. 327 func (se stackElem) newestValue() types.Value { 328 if se.newValue != nil { 329 return se.newValue 330 } 331 return se.oldValue 332 } 333 334 type patchStack struct { 335 vals []stackElem 336 lastPath types.Path 337 addCnt int 338 rmCnt int 339 } 340 341 func (stack *patchStack) push(p types.Path, pp types.PathPart, changeType types.DiffChangeType, oldValue, newValue, newKeyValue types.Value) { 342 stack.vals = append(stack.vals, stackElem{path: p, pathPart: pp, changeType: changeType, oldValue: oldValue, newValue: newValue, newKeyValue: newKeyValue}) 343 } 344 345 func (stack *patchStack) top() *stackElem { 346 return &stack.vals[len(stack.vals)-1] 347 } 348 349 // pop applies the change to the graph. When an element is 'pop'ed from the stack, 350 // this function uses the pathPart to merge that value into it's parent. 351 func (stack *patchStack) pop(ctx context.Context) (stackElem, error) { 352 top := stack.top() 353 stack.vals = stack.vals[:len(stack.vals)-1] 354 if stack.Len() > 0 { 355 newTop := stack.top() 356 parent := newTop.newestValue() 357 358 var err error 359 newTop.newValue, err = stack.updateNode(ctx, top, parent) 360 361 if err != nil { 362 return stackElem{}, err 363 } 364 } 365 return *top, nil 366 } 367 368 func (stack *patchStack) Len() int { 369 return len(stack.vals) 370 } 371 372 // adjustIndexOffset returns an offset that needs to be added to list indexes 373 // when applying diffs to lists. Diffs are applied to lists beginning at the 0th 374 // element. Changes to the list mean that subsequent changes to the same list 375 // have to be adjusted accordingly. The stack keeps state for each list as it's 376 // processed so updateNode() can get the correct index. 377 // Whenever a list is encountered, diffs consist of add & remove operations. The 378 // offset is calculated by keeping a count of each add & remove. Due to the way 379 // way diffs are calculated, no offset is ever needed for 'add' operations. The 380 // offset for 'remove' operations are calculated as: 381 // stack.addCnt - stack.rmCnt 382 func (stack *patchStack) adjustIndexOffset(p types.Path, changeType types.DiffChangeType) (res int) { 383 parentPath := p[:len(p)-1] 384 385 // parentPath is different than the last parentPath so reset counters 386 if stack.lastPath == nil || !stack.lastPath.Equals(parentPath) { 387 stack.lastPath = parentPath 388 stack.addCnt = 0 389 stack.rmCnt = 0 390 } 391 392 // offset for 'Add' operations are always 0, 'Remove' ops offset are 393 // calculated here 394 if changeType == types.DiffChangeRemoved { 395 res = stack.addCnt - stack.rmCnt 396 } 397 398 // Bump up the appropriate cnt for this operation. 399 switch changeType { 400 case types.DiffChangeAdded: 401 stack.addCnt += 1 402 case types.DiffChangeRemoved: 403 stack.rmCnt += 1 404 } 405 return 406 }