github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/diff/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  	"bytes"
    26  	"context"
    27  
    28  	"github.com/dolthub/dolt/go/store/types"
    29  )
    30  
    31  // Patch is a list of difference objects that can be applied to a graph
    32  // using ApplyPatch(). Patch implements a sort order that is useful for
    33  // applying the patch in an efficient way.
    34  type Patch []Difference
    35  
    36  type PatchSort struct {
    37  	patch Patch
    38  }
    39  
    40  func (ps PatchSort) Swap(i, j int) {
    41  	ps.patch[i], ps.patch[j] = ps.patch[j], ps.patch[i]
    42  }
    43  
    44  func (ps PatchSort) Len() int {
    45  	return len(ps.patch)
    46  }
    47  
    48  var vals = map[types.DiffChangeType]int{types.DiffChangeRemoved: 0, types.DiffChangeModified: 1, types.DiffChangeAdded: 2}
    49  
    50  func (ps PatchSort) Less(ctx context.Context, nbf *types.NomsBinFormat, i, j int) (bool, error) {
    51  	if ps.patch[i].Path.Equals(ps.patch[j].Path) {
    52  		return vals[ps.patch[i].ChangeType] < vals[ps.patch[j].ChangeType], nil
    53  	}
    54  	return pathIsLess(ctx, nbf, ps.patch[i].Path, ps.patch[j].Path)
    55  }
    56  
    57  // Utility methods on path
    58  // TODO: Should these be on types.Path & types.PathPart?
    59  func pathIsLess(ctx context.Context, nbf *types.NomsBinFormat, p1, p2 types.Path) (bool, error) {
    60  	for i, pp1 := range p1 {
    61  		if len(p2) == i {
    62  			return false, nil // p1 > p2
    63  		}
    64  
    65  		idx, err := pathPartCompare(ctx, nbf, pp1, p2[i])
    66  
    67  		if err != nil {
    68  			return false, err
    69  		}
    70  
    71  		switch idx {
    72  		case -1:
    73  			return true, nil // p1 < p2
    74  		case 1:
    75  			return false, nil // p1 > p2
    76  		}
    77  	}
    78  
    79  	return len(p2) > len(p1), nil // if true p1 < p2, else p1 == p2
    80  }
    81  
    82  func fieldPathCompare(pp types.FieldPath, o types.PathPart) int {
    83  	switch opp := o.(type) {
    84  	case types.FieldPath:
    85  		if pp.Name == opp.Name {
    86  			return 0
    87  		}
    88  		if pp.Name < opp.Name {
    89  			return -1
    90  		}
    91  		return 1
    92  	case types.IndexPath:
    93  		return -1
    94  	case types.HashIndexPath:
    95  		return -1
    96  	}
    97  	panic("unreachable")
    98  }
    99  
   100  func indexPathCompare(ctx context.Context, nbf *types.NomsBinFormat, pp types.IndexPath, o types.PathPart) (int, error) {
   101  	switch opp := o.(type) {
   102  	case types.FieldPath:
   103  		return 1, nil
   104  	case types.IndexPath:
   105  		if pp.Index.Equals(opp.Index) {
   106  			if pp.IntoKey == opp.IntoKey {
   107  				return 0, nil
   108  			}
   109  			if pp.IntoKey {
   110  				return -1, nil
   111  			}
   112  			return 1, nil
   113  		}
   114  		if isLess, err := pp.Index.Less(ctx, nbf, opp.Index); err != nil {
   115  			return 0, err
   116  		} else if isLess {
   117  			return -1, nil
   118  		}
   119  		return 1, nil
   120  	case types.HashIndexPath:
   121  		return -1, nil
   122  	}
   123  	panic("unreachable")
   124  }
   125  
   126  func hashIndexPathCompare(pp types.HashIndexPath, o types.PathPart) int {
   127  	switch opp := o.(type) {
   128  	case types.FieldPath:
   129  		return 1
   130  	case types.IndexPath:
   131  		return 1
   132  	case types.HashIndexPath:
   133  		switch bytes.Compare(pp.Hash[:], opp.Hash[:]) {
   134  		case -1:
   135  			return -1
   136  		case 0:
   137  			if pp.IntoKey == opp.IntoKey {
   138  				return 0
   139  			}
   140  			if pp.IntoKey {
   141  				return -1
   142  			}
   143  			return 1
   144  		case 1:
   145  			return 1
   146  		}
   147  	}
   148  	panic("unreachable")
   149  }
   150  
   151  func pathPartCompare(ctx context.Context, nbf *types.NomsBinFormat, pp, pp2 types.PathPart) (int, error) {
   152  	switch pp1 := pp.(type) {
   153  	case types.FieldPath:
   154  		return fieldPathCompare(pp1, pp2), nil
   155  	case types.IndexPath:
   156  		return indexPathCompare(ctx, nbf, pp1, pp2)
   157  	case types.HashIndexPath:
   158  		return hashIndexPathCompare(pp1, pp2), nil
   159  	}
   160  	panic("unreachable")
   161  }