github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/merge/merge_test.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  	"encoding/json"
    20  	"sort"
    21  	"testing"
    22  
    23  	"github.com/dolthub/go-mysql-server/sql"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/conflict"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    29  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
    30  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    31  	"github.com/dolthub/dolt/go/libraries/doltcore/ref"
    32  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    33  	"github.com/dolthub/dolt/go/libraries/doltcore/table/editor"
    34  	"github.com/dolthub/dolt/go/libraries/doltcore/table/editor/creation"
    35  	filesys2 "github.com/dolthub/dolt/go/libraries/utils/filesys"
    36  	"github.com/dolthub/dolt/go/libraries/utils/valutil"
    37  	"github.com/dolthub/dolt/go/store/datas"
    38  	"github.com/dolthub/dolt/go/store/pool"
    39  	"github.com/dolthub/dolt/go/store/prolly"
    40  	"github.com/dolthub/dolt/go/store/prolly/tree"
    41  	"github.com/dolthub/dolt/go/store/types"
    42  	"github.com/dolthub/dolt/go/store/val"
    43  )
    44  
    45  const (
    46  	tableName = "test-table"
    47  	name      = "billy bob"
    48  	email     = "bigbillieb@fake.horse"
    49  
    50  	idTag   = 100
    51  	col1Tag = 0
    52  	col2Tag = 1
    53  )
    54  
    55  var colColl = schema.NewColCollection(
    56  	schema.NewColumn("id", idTag, types.IntKind, true, schema.NotNullConstraint{}),
    57  	schema.NewColumn("col1", col1Tag, types.IntKind, false, schema.NotNullConstraint{}),
    58  	schema.NewColumn("col2", col2Tag, types.IntKind, false, schema.NotNullConstraint{}),
    59  )
    60  var sch = schema.MustSchemaFromCols(colColl)
    61  
    62  type rowV struct {
    63  	col1, col2 int
    64  }
    65  
    66  var vD = sch.GetValueDescriptor()
    67  var vB = val.NewTupleBuilder(vD)
    68  var syncPool = pool.NewBuffPool()
    69  
    70  func (v rowV) value() val.Tuple {
    71  	vB.PutInt64(0, int64(v.col1))
    72  	vB.PutInt64(1, int64(v.col2))
    73  	return vB.Build(syncPool)
    74  }
    75  
    76  func (v rowV) nomsValue() types.Value {
    77  	return valsToTestTupleWithoutPks([]types.Value{types.Int(v.col1), types.Int(v.col2)})
    78  }
    79  
    80  const (
    81  	NoopAction ActionType = iota
    82  	InsertAction
    83  	UpdateAction
    84  	DeleteAction
    85  )
    86  
    87  type ActionType int
    88  
    89  type testRow struct {
    90  	key                     int
    91  	initialValue            *rowV
    92  	leftAction, rightAction ActionType
    93  	leftValue, rightValue   *rowV
    94  	conflict                bool
    95  	expectedValue           *rowV
    96  }
    97  
    98  // There are 16 cases for merges if the left and right branches don't modify primary keys.
    99  //
   100  // If a row exists in the ancestor commit, then left and right can perform a no-op, update,
   101  // or delete => 3*3 = +9.
   102  //
   103  // If a row does not exist in the ancestor commit, then left and right can perform a no-op or
   104  // insert => 2*2 = +4.
   105  //
   106  // For (update, update) there are identical updates, conflicting updates, and
   107  // non-conflicting updates. => +2
   108  //
   109  // For (insert, insert) there are identical inserts and conflicting inserts => +1
   110  //
   111  // A modification of a primary key is the combination of the two base cases:
   112  // First, a (delete, delete), then an (insert, insert). We omit tests for these
   113  // and instead defer to the base cases.
   114  
   115  var testRows = []testRow{
   116  	// Ancestor exists
   117  	{
   118  		0,
   119  		&rowV{0, 0},
   120  		NoopAction,
   121  		NoopAction,
   122  		nil,
   123  		nil,
   124  		false,
   125  		&rowV{0, 0},
   126  	},
   127  	{
   128  		1,
   129  		&rowV{1, 1},
   130  		NoopAction,
   131  		UpdateAction,
   132  		nil,
   133  		&rowV{-1, -1},
   134  		false,
   135  		&rowV{-1, -1},
   136  	},
   137  	{
   138  		2,
   139  		&rowV{2, 2},
   140  		NoopAction,
   141  		DeleteAction,
   142  		nil,
   143  		nil,
   144  		false,
   145  		nil,
   146  	},
   147  	{
   148  		3,
   149  		&rowV{3, 3},
   150  		UpdateAction,
   151  		NoopAction,
   152  		&rowV{-3, -3},
   153  		nil,
   154  		false,
   155  		&rowV{-3, -3},
   156  	},
   157  	// Identical Update
   158  	{
   159  		4,
   160  		&rowV{4, 4},
   161  		UpdateAction,
   162  		UpdateAction,
   163  		&rowV{-4, -4},
   164  		&rowV{-4, -4},
   165  		false,
   166  		&rowV{-4, -4},
   167  	},
   168  	// Conflicting Update
   169  	{
   170  		5,
   171  		&rowV{5, 5},
   172  		UpdateAction,
   173  		UpdateAction,
   174  		&rowV{-5, 5},
   175  		&rowV{0, 5},
   176  		true,
   177  		&rowV{-5, 5},
   178  	},
   179  	// Non-conflicting update
   180  	{
   181  		6,
   182  		&rowV{6, 6},
   183  		UpdateAction,
   184  		UpdateAction,
   185  		&rowV{-6, 6},
   186  		&rowV{6, -6},
   187  		false,
   188  		&rowV{-6, -6},
   189  	},
   190  	// Non-conflicting update 2
   191  	{
   192  		62,
   193  		&rowV{62, 62},
   194  		UpdateAction,
   195  		UpdateAction,
   196  		&rowV{-62, 62},
   197  		&rowV{62, -62},
   198  		false,
   199  		&rowV{-62, -62},
   200  	},
   201  	{
   202  		7,
   203  		&rowV{7, 7},
   204  		UpdateAction,
   205  		DeleteAction,
   206  		&rowV{-7, -7},
   207  		nil,
   208  		true,
   209  		&rowV{-7, -7},
   210  	},
   211  	{
   212  		8,
   213  		&rowV{8, 8},
   214  		DeleteAction,
   215  		NoopAction,
   216  		nil,
   217  		nil,
   218  		false,
   219  		nil,
   220  	},
   221  	{
   222  		9,
   223  		&rowV{9, 9},
   224  		DeleteAction,
   225  		UpdateAction,
   226  		nil,
   227  		&rowV{-9, -9},
   228  		true,
   229  		nil,
   230  	},
   231  	{
   232  		10,
   233  		&rowV{10, 10},
   234  		DeleteAction,
   235  		DeleteAction,
   236  		nil,
   237  		nil,
   238  		false,
   239  		nil,
   240  	},
   241  	// Key does not exist in ancestor
   242  	{
   243  		11,
   244  		nil,
   245  		NoopAction,
   246  		NoopAction,
   247  		nil,
   248  		nil,
   249  		false,
   250  		nil,
   251  	},
   252  	{
   253  		12,
   254  		nil,
   255  		NoopAction,
   256  		InsertAction,
   257  		nil,
   258  		&rowV{12, 12},
   259  		false,
   260  		&rowV{12, 12},
   261  	},
   262  	{
   263  		13,
   264  		nil,
   265  		InsertAction,
   266  		NoopAction,
   267  		&rowV{13, 13},
   268  		nil,
   269  		false,
   270  		&rowV{13, 13},
   271  	},
   272  	// Identical Insert
   273  	{
   274  		14,
   275  		nil,
   276  		InsertAction,
   277  		InsertAction,
   278  		&rowV{14, 14},
   279  		&rowV{14, 14},
   280  		false,
   281  		&rowV{14, 14},
   282  	},
   283  	// Conflicting Insert
   284  	{
   285  		15,
   286  		nil,
   287  		InsertAction,
   288  		InsertAction,
   289  		&rowV{15, 15},
   290  		&rowV{15, -15},
   291  		true,
   292  		&rowV{15, 15},
   293  	},
   294  }
   295  
   296  func TestMergeCommits(t *testing.T) {
   297  	if !types.IsFormat_DOLT(types.Format_Default) {
   298  		t.Skip()
   299  	}
   300  
   301  	ddb, vrw, ns, rightCommitHash, ancCommitHash, root, mergeRoot, ancRoot, expectedRows, expectedArtifacts := setupMergeTest(t)
   302  	defer ddb.Close()
   303  	merger, err := NewMerger(root, mergeRoot, ancRoot, rightCommitHash, ancCommitHash, vrw, ns)
   304  	if err != nil {
   305  		t.Fatal(err)
   306  	}
   307  	opts := editor.TestEditorOptions(vrw)
   308  	// TODO: stats
   309  	merged, _, err := merger.MergeTable(sql.NewContext(context.Background()), tableName, opts, MergeOpts{IsCherryPick: false})
   310  	if err != nil {
   311  		t.Fatal(err)
   312  	}
   313  
   314  	ctx := sql.NewEmptyContext()
   315  
   316  	tbl, _, err := root.GetTable(ctx, doltdb.TableName{Name: tableName})
   317  	assert.NoError(t, err)
   318  	sch, err := tbl.GetSchema(ctx)
   319  	assert.NoError(t, err)
   320  	expected, err := doltdb.NewTable(ctx, vrw, ns, sch, expectedRows, nil, nil)
   321  	assert.NoError(t, err)
   322  	expected, err = rebuildAllProllyIndexes(ctx, expected)
   323  	assert.NoError(t, err)
   324  	expected, err = expected.SetArtifacts(ctx, durable.ArtifactIndexFromProllyMap(expectedArtifacts))
   325  	require.NoError(t, err)
   326  
   327  	mergedRows, err := merged.table.GetRowData(ctx)
   328  	assert.NoError(t, err)
   329  
   330  	artIdx, err := merged.table.GetArtifacts(ctx)
   331  	require.NoError(t, err)
   332  	artifacts := durable.ProllyMapFromArtifactIndex(artIdx)
   333  	MustEqualArtifactMap(t, expectedArtifacts, artifacts)
   334  
   335  	MustEqualProlly(t, tableName, durable.ProllyMapFromIndex(expectedRows), durable.ProllyMapFromIndex(mergedRows))
   336  
   337  	for _, index := range sch.Indexes().AllIndexes() {
   338  		mergedIndexRows, err := merged.table.GetIndexRowData(ctx, index.Name())
   339  		require.NoError(t, err)
   340  		expectedIndexRows, err := expected.GetIndexRowData(ctx, index.Name())
   341  		require.NoError(t, err)
   342  		MustEqualProlly(t, index.Name(), durable.ProllyMapFromIndex(expectedIndexRows), durable.ProllyMapFromIndex(mergedIndexRows))
   343  	}
   344  
   345  	h, err := merged.table.HashOf()
   346  	require.NoError(t, err)
   347  	eh, err := expected.HashOf()
   348  	require.NoError(t, err)
   349  	require.Equal(t, eh.String(), h.String(), "table hashes do not equal")
   350  }
   351  
   352  func TestNomsMergeCommits(t *testing.T) {
   353  	if types.IsFormat_DOLT(types.Format_Default) {
   354  		t.Skip()
   355  	}
   356  
   357  	vrw, ns, rightCommitHash, ancCommitHash, root, mergeRoot, ancRoot, expectedRows, expectedConflicts, expectedStats := setupNomsMergeTest(t)
   358  
   359  	merger, err := NewMerger(root, mergeRoot, ancRoot, rightCommitHash, ancCommitHash, vrw, ns)
   360  	if err != nil {
   361  		t.Fatal(err)
   362  	}
   363  	opts := editor.TestEditorOptions(vrw)
   364  	merged, stats, err := merger.MergeTable(sql.NewContext(context.Background()), tableName, opts, MergeOpts{IsCherryPick: false})
   365  	if err != nil {
   366  		t.Fatal(err)
   367  	}
   368  	assert.Equal(t, expectedStats, stats, "received stats is incorrect")
   369  
   370  	tbl, _, err := root.GetTable(context.Background(), doltdb.TableName{Name: tableName})
   371  	assert.NoError(t, err)
   372  	sch, err := tbl.GetSchema(context.Background())
   373  	assert.NoError(t, err)
   374  	expected, err := doltdb.NewNomsTable(context.Background(), vrw, ns, sch, expectedRows, nil, nil)
   375  	assert.NoError(t, err)
   376  	expected, err = editor.RebuildAllIndexes(context.Background(), expected, editor.TestEditorOptions(vrw))
   377  	assert.NoError(t, err)
   378  	conflictSchema := conflict.NewConflictSchema(sch, sch, sch)
   379  	assert.NoError(t, err)
   380  	expected, err = expected.SetConflicts(context.Background(), conflictSchema, durable.ConflictIndexFromNomsMap(expectedConflicts, vrw))
   381  	assert.NoError(t, err)
   382  
   383  	mergedRows, err := merged.table.GetNomsRowData(context.Background())
   384  	assert.NoError(t, err)
   385  	_, confIdx, err := merged.table.GetConflicts(context.Background())
   386  	assert.NoError(t, err)
   387  	conflicts := durable.NomsMapFromConflictIndex(confIdx)
   388  
   389  	if !mergedRows.Equals(expectedRows) {
   390  		t.Error(mustString(types.EncodedValue(context.Background(), expectedRows)), "\n!=\n", mustString(types.EncodedValue(context.Background(), mergedRows)))
   391  	}
   392  	if !conflicts.Equals(expectedConflicts) {
   393  		t.Error(mustString(types.EncodedValue(context.Background(), expectedConflicts)), "\n!=\n", mustString(types.EncodedValue(context.Background(), conflicts)))
   394  	}
   395  
   396  	for _, index := range sch.Indexes().AllIndexes() {
   397  		mergedIndexRows, err := merged.table.GetNomsIndexRowData(context.Background(), index.Name())
   398  		assert.NoError(t, err)
   399  		expectedIndexRows, err := expected.GetNomsIndexRowData(context.Background(), index.Name())
   400  		assert.NoError(t, err)
   401  		assert.Equal(t, expectedRows.Len(), mergedIndexRows.Len(), "index %s incorrect row count", index.Name())
   402  		assert.Truef(t, expectedIndexRows.Equals(mergedIndexRows),
   403  			"index %s contents incorrect.\nExpected: \n%s\nReceived: \n%s\n", index.Name(),
   404  			mustString(types.EncodedValue(context.Background(), expectedIndexRows)),
   405  			mustString(types.EncodedValue(context.Background(), mergedIndexRows)))
   406  	}
   407  
   408  	h, err := merged.table.HashOf()
   409  	assert.NoError(t, err)
   410  	eh, err := expected.HashOf()
   411  	assert.NoError(t, err)
   412  	assert.Equal(t, eh.String(), h.String(), "table hashes do not equal")
   413  }
   414  
   415  func sortTests(t []testRow) {
   416  	sort.Slice(t, func(i, j int) bool {
   417  		return t[i].key < t[j].key
   418  	})
   419  }
   420  
   421  func setupMergeTest(t *testing.T) (*doltdb.DoltDB, types.ValueReadWriter, tree.NodeStore, doltdb.Rootish, doltdb.Rootish, doltdb.RootValue, doltdb.RootValue, doltdb.RootValue, durable.Index, prolly.ArtifactMap) {
   422  	ddb := mustMakeEmptyRepo(t)
   423  	vrw := ddb.ValueReadWriter()
   424  	ns := ddb.NodeStore()
   425  	sortTests(testRows)
   426  
   427  	var initialKVs []val.Tuple
   428  	var expectedKVs []val.Tuple
   429  
   430  	for _, testCase := range testRows {
   431  		if testCase.initialValue != nil {
   432  			initialKVs = append(initialKVs, key(testCase.key), testCase.initialValue.value())
   433  		}
   434  		if testCase.expectedValue != nil {
   435  			expectedKVs = append(expectedKVs, key(testCase.key), testCase.expectedValue.value())
   436  		}
   437  	}
   438  
   439  	initialRows, err := prolly.NewMapFromTuples(context.Background(), ns, kD, vD, initialKVs...)
   440  	require.NoError(t, err)
   441  	expectedRows, err := prolly.NewMapFromTuples(context.Background(), ns, kD, vD, expectedKVs...)
   442  	require.NoError(t, err)
   443  
   444  	leftMut := initialRows.Mutate()
   445  	rightMut := initialRows.Mutate()
   446  	for _, testCase := range testRows {
   447  
   448  		switch testCase.leftAction {
   449  		case NoopAction:
   450  			break
   451  		case InsertAction, UpdateAction:
   452  			err = leftMut.Put(context.Background(), key(testCase.key), testCase.leftValue.value())
   453  			require.NoError(t, err)
   454  		case DeleteAction:
   455  			err = leftMut.Delete(context.Background(), key(testCase.key))
   456  			require.NoError(t, err)
   457  		}
   458  
   459  		switch testCase.rightAction {
   460  		case NoopAction:
   461  			break
   462  		case InsertAction, UpdateAction:
   463  			err = rightMut.Put(context.Background(), key(testCase.key), testCase.rightValue.value())
   464  			require.NoError(t, err)
   465  		case DeleteAction:
   466  			err = rightMut.Delete(context.Background(), key(testCase.key))
   467  			require.NoError(t, err)
   468  		}
   469  	}
   470  
   471  	ctx := sql.NewEmptyContext()
   472  
   473  	updatedRows, err := leftMut.Map(ctx)
   474  	require.NoError(t, err)
   475  	mergeRows, err := rightMut.Map(ctx)
   476  	require.NoError(t, err)
   477  
   478  	rootTbl, err := doltdb.NewTable(ctx, vrw, ns, sch, durable.IndexFromProllyMap(updatedRows), nil, nil)
   479  	require.NoError(t, err)
   480  	rootTbl, err = rebuildAllProllyIndexes(ctx, rootTbl)
   481  	require.NoError(t, err)
   482  
   483  	mergeTbl, err := doltdb.NewTable(ctx, vrw, ns, sch, durable.IndexFromProllyMap(mergeRows), nil, nil)
   484  	require.NoError(t, err)
   485  	mergeTbl, err = rebuildAllProllyIndexes(ctx, mergeTbl)
   486  	require.NoError(t, err)
   487  
   488  	ancTbl, err := doltdb.NewTable(ctx, vrw, ns, sch, durable.IndexFromProllyMap(initialRows), nil, nil)
   489  	require.NoError(t, err)
   490  	ancTbl, err = rebuildAllProllyIndexes(ctx, ancTbl)
   491  	require.NoError(t, err)
   492  
   493  	rightCm, baseCm, root, mergeRoot, ancRoot := buildLeftRightAncCommitsAndBranches(t, ddb, rootTbl, mergeTbl, ancTbl)
   494  
   495  	artifactMap, err := prolly.NewArtifactMapFromTuples(ctx, ns, kD)
   496  	require.NoError(t, err)
   497  	artEditor := artifactMap.Editor()
   498  
   499  	baseCmHash, err := baseCm.HashOf()
   500  	require.NoError(t, err)
   501  	rightCmHash, err := rightCm.HashOf()
   502  	require.NoError(t, err)
   503  
   504  	m := prolly.ConflictMetadata{
   505  		BaseRootIsh: baseCmHash,
   506  	}
   507  	d, err := json.Marshal(m)
   508  	require.NoError(t, err)
   509  
   510  	for _, testCase := range testRows {
   511  		if testCase.conflict {
   512  			err = artEditor.Add(ctx, key(testCase.key), rightCmHash, prolly.ArtifactTypeConflict, d)
   513  			require.NoError(t, err)
   514  		}
   515  	}
   516  
   517  	expectedArtifacts, err := artEditor.Flush(ctx)
   518  	require.NoError(t, err)
   519  
   520  	return ddb, vrw, ns, rightCm, baseCm, root, mergeRoot, ancRoot, durable.IndexFromProllyMap(expectedRows), expectedArtifacts
   521  }
   522  
   523  func setupNomsMergeTest(t *testing.T) (types.ValueReadWriter, tree.NodeStore, doltdb.Rootish, doltdb.Rootish, doltdb.RootValue, doltdb.RootValue, doltdb.RootValue, types.Map, types.Map, *MergeStats) {
   524  	ddb := mustMakeEmptyRepo(t)
   525  	vrw := ddb.ValueReadWriter()
   526  	ns := ddb.NodeStore()
   527  	sortTests(testRows)
   528  
   529  	var initalKVs []types.Value
   530  	var expectedKVs []types.Value
   531  	var expectedConflictsKVs []types.Value
   532  	for _, testCase := range testRows {
   533  		if testCase.initialValue != nil {
   534  			initalKVs = append(initalKVs, nomsKey(testCase.key), testCase.initialValue.nomsValue())
   535  		}
   536  		if testCase.expectedValue != nil {
   537  			expectedKVs = append(expectedKVs, nomsKey(testCase.key), testCase.expectedValue.nomsValue())
   538  		}
   539  		if testCase.conflict {
   540  			expectedConflictsKVs = append(
   541  				expectedConflictsKVs,
   542  				nomsKey(testCase.key),
   543  				mustTuple(conflict.NewConflict(
   544  					unwrapNoms(testCase.initialValue),
   545  					unwrapNoms(testCase.leftValue),
   546  					unwrapNoms(testCase.rightValue),
   547  				).ToNomsList(vrw)),
   548  			)
   549  		}
   550  	}
   551  	initialRows, err := types.NewMap(context.Background(), vrw, initalKVs...)
   552  	require.NoError(t, err)
   553  	expectedRows, err := types.NewMap(context.Background(), vrw, expectedKVs...)
   554  	require.NoError(t, err)
   555  	expectedConflicts, err := types.NewMap(context.Background(), vrw, expectedConflictsKVs...)
   556  	require.NoError(t, err)
   557  
   558  	leftE := initialRows.Edit()
   559  	rightE := initialRows.Edit()
   560  	for _, testCase := range testRows {
   561  		switch testCase.leftAction {
   562  		case NoopAction:
   563  			break
   564  		case InsertAction, UpdateAction:
   565  			leftE.Set(nomsKey(testCase.key), testCase.leftValue.nomsValue())
   566  		case DeleteAction:
   567  			leftE.Remove(nomsKey(testCase.key))
   568  		}
   569  
   570  		switch testCase.rightAction {
   571  		case NoopAction:
   572  			break
   573  		case InsertAction, UpdateAction:
   574  			rightE.Set(nomsKey(testCase.key), testCase.rightValue.nomsValue())
   575  		case DeleteAction:
   576  			rightE.Remove(nomsKey(testCase.key))
   577  		}
   578  	}
   579  
   580  	updatedRows, err := leftE.Map(context.Background())
   581  	require.NoError(t, err)
   582  	mergeRows, err := rightE.Map(context.Background())
   583  	require.NoError(t, err)
   584  
   585  	tbl, err := doltdb.NewNomsTable(context.Background(), vrw, ns, sch, initialRows, nil, nil)
   586  	require.NoError(t, err)
   587  	tbl, err = editor.RebuildAllIndexes(context.Background(), tbl, editor.TestEditorOptions(vrw))
   588  	require.NoError(t, err)
   589  
   590  	updatedTbl, err := doltdb.NewNomsTable(context.Background(), vrw, ns, sch, updatedRows, nil, nil)
   591  	require.NoError(t, err)
   592  	updatedTbl, err = editor.RebuildAllIndexes(context.Background(), updatedTbl, editor.TestEditorOptions(vrw))
   593  	require.NoError(t, err)
   594  
   595  	mergeTbl, err := doltdb.NewNomsTable(context.Background(), vrw, ns, sch, mergeRows, nil, nil)
   596  	require.NoError(t, err)
   597  	mergeTbl, err = editor.RebuildAllIndexes(context.Background(), mergeTbl, editor.TestEditorOptions(vrw))
   598  	require.NoError(t, err)
   599  
   600  	ancTable, err := doltdb.NewNomsTable(context.Background(), vrw, ns, sch, initialRows, nil, nil)
   601  	require.NoError(t, err)
   602  	ancTable, err = editor.RebuildAllIndexes(context.Background(), ancTable, editor.TestEditorOptions(vrw))
   603  	require.NoError(t, err)
   604  
   605  	rightCm, ancCommit, root, mergeRoot, ancRoot := buildLeftRightAncCommitsAndBranches(t, ddb, updatedTbl, mergeTbl, ancTable)
   606  
   607  	return vrw, ns, rightCm, ancCommit, root, mergeRoot, ancRoot, expectedRows, expectedConflicts, calcExpectedStats(t)
   608  }
   609  
   610  // rebuildAllProllyIndexes builds the data for the secondary indexes in |tbl|'s
   611  // schema.
   612  func rebuildAllProllyIndexes(ctx *sql.Context, tbl *doltdb.Table) (*doltdb.Table, error) {
   613  	sch, err := tbl.GetSchema(ctx)
   614  	if err != nil {
   615  		return nil, err
   616  	}
   617  
   618  	if sch.Indexes().Count() == 0 {
   619  		return tbl, nil
   620  	}
   621  
   622  	indexes, err := tbl.GetIndexSet(ctx)
   623  	if err != nil {
   624  		return nil, err
   625  	}
   626  
   627  	tableRowData, err := tbl.GetRowData(ctx)
   628  	if err != nil {
   629  		return nil, err
   630  	}
   631  	primary := durable.ProllyMapFromIndex(tableRowData)
   632  
   633  	for _, index := range sch.Indexes().AllIndexes() {
   634  		rebuiltIndexRowData, err := creation.BuildSecondaryProllyIndex(ctx, tbl.ValueReadWriter(), tbl.NodeStore(), sch, tableName, index, primary)
   635  		if err != nil {
   636  			return nil, err
   637  		}
   638  
   639  		indexes, err = indexes.PutIndex(ctx, index.Name(), rebuiltIndexRowData)
   640  		if err != nil {
   641  			return nil, err
   642  		}
   643  	}
   644  
   645  	return tbl.SetIndexSet(ctx, indexes)
   646  }
   647  
   648  func calcExpectedStats(t *testing.T) *MergeStats {
   649  	s := &MergeStats{Operation: TableModified}
   650  	for _, testCase := range testRows {
   651  		if (testCase.leftAction == InsertAction) != (testCase.rightAction == InsertAction) {
   652  			if testCase.leftAction == UpdateAction || testCase.rightAction == UpdateAction ||
   653  				testCase.leftAction == DeleteAction || testCase.rightAction == DeleteAction {
   654  				// Either the row exists in the ancestor commit and we are
   655  				// deleting or updating it, or the row doesn't exist and we are
   656  				// inserting it.
   657  				t.Fatalf("it's impossible for an insert to be paired with an update or delete")
   658  			}
   659  		}
   660  
   661  		if testCase.leftAction == NoopAction {
   662  			switch testCase.rightAction {
   663  			case NoopAction:
   664  			case DeleteAction:
   665  				s.Deletes++
   666  			case InsertAction:
   667  				s.Adds++
   668  			case UpdateAction:
   669  				s.Modifications++
   670  			}
   671  			continue
   672  		}
   673  
   674  		if testCase.rightAction == NoopAction {
   675  			switch testCase.leftAction {
   676  			case NoopAction:
   677  			case DeleteAction:
   678  				s.Deletes++
   679  			case InsertAction:
   680  				s.Adds++
   681  			case UpdateAction:
   682  				s.Modifications++
   683  			}
   684  			continue
   685  		}
   686  
   687  		if testCase.conflict {
   688  			// (UpdateAction, DeleteAction),
   689  			// (DeleteAction, UpdateAction),
   690  			// (UpdateAction, UpdateAction) with conflict,
   691  			// (InsertAction, InsertAction) with conflict
   692  			s.DataConflicts++
   693  			continue
   694  		}
   695  
   696  		if testCase.leftAction == InsertAction && testCase.rightAction == InsertAction {
   697  			// Equivalent inserts
   698  			continue
   699  		}
   700  
   701  		if !valutil.NilSafeEqCheck(unwrapNoms(testCase.leftValue), unwrapNoms(testCase.rightValue)) {
   702  			s.Modifications++
   703  			continue
   704  		}
   705  	}
   706  
   707  	return s
   708  }
   709  
   710  func mustMakeEmptyRepo(t *testing.T) *doltdb.DoltDB {
   711  	ddb, _ := doltdb.LoadDoltDB(context.Background(), types.Format_Default, doltdb.InMemDoltDB, filesys2.LocalFS)
   712  	err := ddb.WriteEmptyRepo(context.Background(), env.DefaultInitBranch, name, email)
   713  	require.NoError(t, err)
   714  	return ddb
   715  }
   716  
   717  func buildLeftRightAncCommitsAndBranches(t *testing.T, ddb *doltdb.DoltDB, rootTbl, mergeTbl, ancTbl *doltdb.Table) (doltdb.Rootish, doltdb.Rootish, doltdb.RootValue, doltdb.RootValue, doltdb.RootValue) {
   718  	mainHeadSpec, _ := doltdb.NewCommitSpec(env.DefaultInitBranch)
   719  	optCmt, err := ddb.Resolve(context.Background(), mainHeadSpec, nil)
   720  	require.NoError(t, err)
   721  	mainHead, ok := optCmt.ToCommit()
   722  	require.True(t, ok)
   723  
   724  	mRoot, err := mainHead.GetRootValue(context.Background())
   725  	require.NoError(t, err)
   726  
   727  	mRoot, err = mRoot.PutTable(context.Background(), doltdb.TableName{Name: tableName}, ancTbl)
   728  	require.NoError(t, err)
   729  
   730  	updatedRoot, err := mRoot.PutTable(context.Background(), doltdb.TableName{Name: tableName}, rootTbl)
   731  	require.NoError(t, err)
   732  
   733  	mergeRoot, err := mRoot.PutTable(context.Background(), doltdb.TableName{Name: tableName}, mergeTbl)
   734  	require.NoError(t, err)
   735  
   736  	r, mainHash, err := ddb.WriteRootValue(context.Background(), mRoot)
   737  	require.NoError(t, err)
   738  	mRoot = r
   739  	r, hash, err := ddb.WriteRootValue(context.Background(), updatedRoot)
   740  	require.NoError(t, err)
   741  	updatedRoot = r
   742  	r, mergeHash, err := ddb.WriteRootValue(context.Background(), mergeRoot)
   743  	require.NoError(t, err)
   744  	mergeRoot = r
   745  
   746  	meta, err := datas.NewCommitMeta(name, email, "fake")
   747  	require.NoError(t, err)
   748  	initialCommit, err := ddb.Commit(context.Background(), mainHash, ref.NewBranchRef(env.DefaultInitBranch), meta)
   749  	require.NoError(t, err)
   750  	commit, err := ddb.Commit(context.Background(), hash, ref.NewBranchRef(env.DefaultInitBranch), meta)
   751  	require.NoError(t, err)
   752  
   753  	err = ddb.NewBranchAtCommit(context.Background(), ref.NewBranchRef("to-merge"), initialCommit, nil)
   754  	require.NoError(t, err)
   755  	mergeCommit, err := ddb.Commit(context.Background(), mergeHash, ref.NewBranchRef("to-merge"), meta)
   756  	require.NoError(t, err)
   757  
   758  	root, err := commit.GetRootValue(context.Background())
   759  	require.NoError(t, err)
   760  
   761  	optCmt, err = doltdb.GetCommitAncestor(context.Background(), commit, mergeCommit)
   762  	require.NoError(t, err)
   763  	ancCm, ok := optCmt.ToCommit()
   764  	require.True(t, ok)
   765  
   766  	ancRoot, err := ancCm.GetRootValue(context.Background())
   767  	require.NoError(t, err)
   768  
   769  	ff, err := commit.CanFastForwardTo(context.Background(), mergeCommit)
   770  	require.NoError(t, err)
   771  	require.False(t, ff)
   772  
   773  	return mergeCommit, ancCm, root, mergeRoot, ancRoot
   774  }
   775  
   776  var kD = sch.GetKeyDescriptor()
   777  var kB = val.NewTupleBuilder(kD)
   778  
   779  func key(i int) val.Tuple {
   780  	kB.PutInt64(0, int64(i))
   781  	return kB.Build(syncPool)
   782  }
   783  
   784  func nomsKey(i int) types.Value {
   785  	return mustTuple(types.NewTuple(types.Format_Default, types.Uint(idTag), types.Int(i)))
   786  }
   787  
   788  func unwrap(v *rowV) val.Tuple {
   789  	if v == nil {
   790  		return nil
   791  	}
   792  	return v.value()
   793  }
   794  
   795  func unwrapNoms(v *rowV) types.Value {
   796  	if v == nil {
   797  		return nil
   798  	}
   799  	return v.nomsValue()
   800  }
   801  
   802  func mustTuple(tpl types.Tuple, err error) types.Tuple {
   803  	if err != nil {
   804  		panic(err)
   805  	}
   806  
   807  	return tpl
   808  }
   809  
   810  func mustString(str string, err error) string {
   811  	if err != nil {
   812  		panic(err)
   813  	}
   814  
   815  	return str
   816  }
   817  
   818  func MustDebugFormatProlly(t *testing.T, m prolly.Map) string {
   819  	s, err := prolly.DebugFormat(context.Background(), m)
   820  	require.NoError(t, err)
   821  	return s
   822  }
   823  
   824  func MustEqualProlly(t *testing.T, name string, expected, actual prolly.Map) {
   825  	require.Equal(t, expected.HashOf(), actual.HashOf(),
   826  		"hashes differed for %s. expected: %s\nactual: %s", name, MustDebugFormatProlly(t, expected), MustDebugFormatProlly(t, actual))
   827  }
   828  
   829  func MustEqualArtifactMap(t *testing.T, expected prolly.ArtifactMap, actual prolly.ArtifactMap) {
   830  	require.Equal(t, expected.HashOf(), actual.HashOf(),
   831  		"artifact map hashes differed.")
   832  }