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  }