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  }