github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/merge/schema_integration_test.go (about)

     1  // Copyright 2020 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_test
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/require"
    23  
    24  	"github.com/dolthub/dolt/go/cmd/dolt/cli"
    25  	"github.com/dolthub/dolt/go/cmd/dolt/commands"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    29  	"github.com/dolthub/dolt/go/libraries/doltcore/merge"
    30  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    31  	"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
    32  )
    33  
    34  type testCommand struct {
    35  	cmd  cli.Command
    36  	args args
    37  }
    38  
    39  func (tc testCommand) exec(t *testing.T, ctx context.Context, dEnv *env.DoltEnv) {
    40  	exitCode := tc.cmd.Exec(ctx, tc.cmd.Name(), tc.args, dEnv)
    41  	require.Equal(t, 0, exitCode)
    42  }
    43  
    44  type args []string
    45  
    46  func TestMergeSchemas(t *testing.T) {
    47  	for _, test := range mergeSchemaTests {
    48  		t.Run(test.name, func(t *testing.T) {
    49  			testMergeSchemas(t, test)
    50  		})
    51  	}
    52  	for _, test := range mergeSchemaConflictTests {
    53  		t.Run(test.name, func(t *testing.T) {
    54  			testMergeSchemasWithConflicts(t, test)
    55  		})
    56  	}
    57  	for _, test := range mergeForeignKeyTests {
    58  		t.Run(test.name, func(t *testing.T) {
    59  			testMergeForeignKeys(t, test)
    60  		})
    61  	}
    62  }
    63  
    64  type mergeSchemaTest struct {
    65  	name  string
    66  	setup []testCommand
    67  	sch   schema.Schema
    68  }
    69  
    70  type mergeSchemaConflictTest struct {
    71  	name        string
    72  	setup       []testCommand
    73  	expConflict merge.SchemaConflict
    74  }
    75  
    76  type mergeForeignKeyTest struct {
    77  	name          string
    78  	setup         []testCommand
    79  	fkColl        *doltdb.ForeignKeyCollection
    80  	expFKConflict []merge.FKConflict
    81  }
    82  
    83  var setupCommon = []testCommand{
    84  	{commands.SqlCmd{}, []string{"-q", "create table test (" +
    85  		"pk int not null primary key," +
    86  		"c1 int not null," +
    87  		"c2 int," +
    88  		"c3 int);"}},
    89  	{commands.SqlCmd{}, []string{"-q", "create index c1_idx on test(c1)"}},
    90  	{commands.AddCmd{}, []string{"."}},
    91  	{commands.CommitCmd{}, []string{"-m", "setup common"}},
    92  	{commands.BranchCmd{}, []string{"other"}},
    93  }
    94  
    95  var mergeSchemaTests = []mergeSchemaTest{
    96  	{
    97  		name:  "no changes",
    98  		setup: []testCommand{},
    99  		sch: schemaFromColsAndIdxs(
   100  			colCollection(
   101  				newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}),
   102  				newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}),
   103  				newColTypeInfo("c2", uint64(8539), typeinfo.Int32Type, false),
   104  				newColTypeInfo("c3", uint64(4696), typeinfo.Int32Type, false)),
   105  			schema.NewIndex("c1_idx", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   106  		),
   107  	},
   108  	{
   109  		name: "add cols, drop cols, merge",
   110  		setup: []testCommand{
   111  			{commands.SqlCmd{}, []string{"-q", "alter table test drop column c2;"}},
   112  			{commands.SqlCmd{}, []string{"-q", "alter table test add column c8 int;"}},
   113  			{commands.AddCmd{}, []string{"."}},
   114  			{commands.CommitCmd{}, []string{"-m", "modified branch master"}},
   115  			{commands.CheckoutCmd{}, []string{"other"}},
   116  			{commands.SqlCmd{}, []string{"-q", "alter table test drop column c3;"}},
   117  			{commands.SqlCmd{}, []string{"-q", "alter table test add column c9 int;"}},
   118  			{commands.AddCmd{}, []string{"."}},
   119  			{commands.CommitCmd{}, []string{"-m", "modified branch other"}},
   120  			{commands.CheckoutCmd{}, []string{"master"}},
   121  		},
   122  		sch: schemaFromColsAndIdxs(
   123  			colCollection(
   124  				newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}),
   125  				newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}),
   126  				newColTypeInfo("c8", uint64(12393), typeinfo.Int32Type, false),
   127  				newColTypeInfo("c9", uint64(4508), typeinfo.Int32Type, false)),
   128  			schema.NewIndex("c1_idx", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   129  		),
   130  	},
   131  	{
   132  		name: "add constraint, drop constraint, merge",
   133  		setup: []testCommand{
   134  			{commands.SqlCmd{}, []string{"-q", "alter table test modify c1 int null;"}},
   135  			{commands.AddCmd{}, []string{"."}},
   136  			{commands.CommitCmd{}, []string{"-m", "modified branch master"}},
   137  			{commands.CheckoutCmd{}, []string{"other"}},
   138  			{commands.SqlCmd{}, []string{"-q", "alter table test modify c2 int not null;"}},
   139  			{commands.AddCmd{}, []string{"."}},
   140  			{commands.CommitCmd{}, []string{"-m", "modified branch other"}},
   141  			{commands.CheckoutCmd{}, []string{"master"}},
   142  		},
   143  		sch: schemaFromColsAndIdxs(
   144  			colCollection(
   145  				newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}),
   146  				newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false),
   147  				newColTypeInfo("c2", uint64(8539), typeinfo.Int32Type, false, schema.NotNullConstraint{}),
   148  				newColTypeInfo("c3", uint64(4696), typeinfo.Int32Type, false)),
   149  			schema.NewIndex("c1_idx", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   150  		),
   151  	},
   152  	{
   153  		name: "add index, drop index, merge",
   154  		setup: []testCommand{
   155  			{commands.SqlCmd{}, []string{"-q", "create index c3_idx on test(c3);"}},
   156  			{commands.AddCmd{}, []string{"."}},
   157  			{commands.CommitCmd{}, []string{"-m", "modified branch master"}},
   158  			{commands.CheckoutCmd{}, []string{"other"}},
   159  			{commands.SqlCmd{}, []string{"-q", "alter table test drop index c1_idx;"}},
   160  			{commands.AddCmd{}, []string{"."}},
   161  			{commands.CommitCmd{}, []string{"-m", "modified branch other"}},
   162  			{commands.CheckoutCmd{}, []string{"master"}},
   163  		},
   164  		sch: schemaFromColsAndIdxs(
   165  			colCollection(
   166  				newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}),
   167  				newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}),
   168  				newColTypeInfo("c2", uint64(8539), typeinfo.Int32Type, false),
   169  				newColTypeInfo("c3", uint64(4696), typeinfo.Int32Type, false)),
   170  			schema.NewIndex("c3_idx", []uint64{4696}, []uint64{4696, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   171  		),
   172  	},
   173  	{
   174  		name: "rename columns",
   175  		setup: []testCommand{
   176  			{commands.SqlCmd{}, []string{"-q", "alter table test rename column c3 to c33;"}},
   177  			{commands.AddCmd{}, []string{"."}},
   178  			{commands.CommitCmd{}, []string{"-m", "modified branch master"}},
   179  			{commands.CheckoutCmd{}, []string{"other"}},
   180  			{commands.SqlCmd{}, []string{"-q", "alter table test rename column c2 to c22;"}},
   181  			{commands.AddCmd{}, []string{"."}},
   182  			{commands.CommitCmd{}, []string{"-m", "modified branch other"}},
   183  			{commands.CheckoutCmd{}, []string{"master"}},
   184  		},
   185  		sch: schemaFromColsAndIdxs(
   186  			colCollection(
   187  				newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}),
   188  				newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}),
   189  				newColTypeInfo("c22", uint64(8539), typeinfo.Int32Type, false),
   190  				newColTypeInfo("c33", uint64(4696), typeinfo.Int32Type, false)),
   191  			schema.NewIndex("c1_idx", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   192  		),
   193  	},
   194  	{
   195  		name: "rename indexes",
   196  		setup: []testCommand{
   197  			{commands.SqlCmd{}, []string{"-q", "alter table test drop index c1_idx;"}},
   198  			{commands.SqlCmd{}, []string{"-q", "create index c1_index on test(c1);"}},
   199  			{commands.AddCmd{}, []string{"."}},
   200  			{commands.CommitCmd{}, []string{"-m", "modified branch master"}},
   201  		},
   202  		sch: schemaFromColsAndIdxs(
   203  			colCollection(
   204  				newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}),
   205  				newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}),
   206  				newColTypeInfo("c2", uint64(8539), typeinfo.Int32Type, false),
   207  				newColTypeInfo("c3", uint64(4696), typeinfo.Int32Type, false)),
   208  			schema.NewIndex("c1_index", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   209  		),
   210  	},
   211  	{
   212  		name: "add same column on both branches, merge",
   213  		setup: []testCommand{
   214  			{commands.SqlCmd{}, []string{"-q", "alter table test add column c4 int;"}},
   215  			{commands.AddCmd{}, []string{"."}},
   216  			{commands.CommitCmd{}, []string{"-m", "modified branch master"}},
   217  			{commands.CheckoutCmd{}, []string{"other"}},
   218  			{commands.SqlCmd{}, []string{"-q", "alter table test add column c4 int;"}},
   219  			{commands.AddCmd{}, []string{"."}},
   220  			{commands.CommitCmd{}, []string{"-m", "modified branch other"}},
   221  			{commands.CheckoutCmd{}, []string{"master"}},
   222  		},
   223  		sch: schemaFromColsAndIdxs(
   224  			colCollection(
   225  				newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}),
   226  				newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}),
   227  				newColTypeInfo("c2", uint64(8539), typeinfo.Int32Type, false),
   228  				newColTypeInfo("c3", uint64(4696), typeinfo.Int32Type, false),
   229  				newColTypeInfo("c4", uint64(1716), typeinfo.Int32Type, false)),
   230  			schema.NewIndex("c1_idx", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   231  		),
   232  	},
   233  	{
   234  		name: "add same index on both branches, merge",
   235  		setup: []testCommand{
   236  			{commands.SqlCmd{}, []string{"-q", "create index c3_idx on test(c3);"}},
   237  			{commands.AddCmd{}, []string{"."}},
   238  			{commands.CommitCmd{}, []string{"-m", "modified branch master"}},
   239  			{commands.CheckoutCmd{}, []string{"other"}},
   240  			{commands.SqlCmd{}, []string{"-q", "create index c3_idx on test(c3);"}},
   241  			{commands.AddCmd{}, []string{"."}},
   242  			{commands.CommitCmd{}, []string{"-m", "modified branch other"}},
   243  			{commands.CheckoutCmd{}, []string{"master"}},
   244  		},
   245  		sch: schemaFromColsAndIdxs(
   246  			colCollection(
   247  				newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}),
   248  				newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}),
   249  				newColTypeInfo("c2", uint64(8539), typeinfo.Int32Type, false),
   250  				newColTypeInfo("c3", uint64(4696), typeinfo.Int32Type, false)),
   251  			schema.NewIndex("c1_idx", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   252  			schema.NewIndex("c3_idx", []uint64{4696}, []uint64{4696, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   253  		),
   254  	},
   255  }
   256  
   257  var mergeSchemaConflictTests = []mergeSchemaConflictTest{
   258  	{
   259  		name: "no conflicts",
   260  		expConflict: merge.SchemaConflict{
   261  			TableName: "test",
   262  		},
   263  	},
   264  	{
   265  		name: "column name collisions",
   266  		setup: []testCommand{
   267  			{commands.SqlCmd{}, []string{"-q", "alter table test rename column c3 to c4;"}},
   268  			{commands.SqlCmd{}, []string{"-q", "alter table test add column C6 int;"}},
   269  			{commands.AddCmd{}, []string{"."}},
   270  			{commands.CommitCmd{}, []string{"-m", "modified branch master"}},
   271  			{commands.CheckoutCmd{}, []string{"other"}},
   272  			{commands.SqlCmd{}, []string{"-q", "alter table test rename column c2 to c4;"}},
   273  			{commands.SqlCmd{}, []string{"-q", "alter table test add column c6 int;"}},
   274  			{commands.AddCmd{}, []string{"."}},
   275  			{commands.CommitCmd{}, []string{"-m", "modified branch other"}},
   276  			{commands.CheckoutCmd{}, []string{"master"}},
   277  		},
   278  		expConflict: merge.SchemaConflict{
   279  			TableName: "test",
   280  			ColConflicts: []merge.ColConflict{
   281  				{
   282  					Kind:   merge.NameCollision,
   283  					Ours:   newColTypeInfo("c4", uint64(3), typeinfo.Int32Type, false),
   284  					Theirs: newColTypeInfo("c4", uint64(2), typeinfo.Int32Type, false),
   285  				},
   286  				{
   287  					Kind:   merge.NameCollision,
   288  					Ours:   newColTypeInfo("C6", uint64(13), typeinfo.Int32Type, false),
   289  					Theirs: newColTypeInfo("c6", uint64(19), typeinfo.Int32Type, false),
   290  				},
   291  			},
   292  		},
   293  	},
   294  	{
   295  		name: "index name collisions",
   296  		setup: []testCommand{
   297  			{commands.SqlCmd{}, []string{"-q", "create index both on test (c1,c2);"}},
   298  			{commands.AddCmd{}, []string{"."}},
   299  			{commands.CommitCmd{}, []string{"-m", "modified branch master"}},
   300  			{commands.CheckoutCmd{}, []string{"other"}},
   301  			{commands.SqlCmd{}, []string{"-q", "create index both on test (c2, c3);"}},
   302  			{commands.AddCmd{}, []string{"."}},
   303  			{commands.CommitCmd{}, []string{"-m", "modified branch other"}},
   304  			{commands.CheckoutCmd{}, []string{"master"}},
   305  		},
   306  		expConflict: merge.SchemaConflict{
   307  			TableName: "test",
   308  			IdxConflicts: []merge.IdxConflict{
   309  				{
   310  					Kind:   merge.NameCollision,
   311  					Ours:   schema.NewIndex("both", []uint64{8201, 8539}, []uint64{8201, 8539, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   312  					Theirs: schema.NewIndex("both", []uint64{8539, 4696}, []uint64{8539, 4696, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   313  				},
   314  			},
   315  		},
   316  	},
   317  	{
   318  		name: "column definition collision",
   319  		setup: []testCommand{
   320  			{commands.SqlCmd{}, []string{"-q", "alter table test add column c40 int;"}},
   321  			{commands.SqlCmd{}, []string{"-q", "alter table test add column c6 bigint;"}},
   322  			{commands.AddCmd{}, []string{"."}},
   323  			{commands.CommitCmd{}, []string{"-m", "modified branch master"}},
   324  			{commands.CheckoutCmd{}, []string{"other"}},
   325  			{commands.SqlCmd{}, []string{"-q", "alter table test add column c40 int;"}},
   326  			{commands.SqlCmd{}, []string{"-q", "alter table test rename column c40 to c44;"}},
   327  			{commands.SqlCmd{}, []string{"-q", "alter table test add column c6 tinyint;"}},
   328  			{commands.AddCmd{}, []string{"."}},
   329  			{commands.CommitCmd{}, []string{"-m", "modified branch other"}},
   330  			{commands.CheckoutCmd{}, []string{"master"}},
   331  		},
   332  		expConflict: merge.SchemaConflict{
   333  			TableName: "test",
   334  			ColConflicts: []merge.ColConflict{
   335  				{
   336  					Kind:   merge.TagCollision,
   337  					Ours:   newColTypeInfo("c40", uint64(679), typeinfo.Int32Type, false),
   338  					Theirs: newColTypeInfo("c44", uint64(679), typeinfo.Int32Type, false),
   339  				},
   340  				{
   341  					Kind:   merge.TagCollision,
   342  					Ours:   newColTypeInfo("c6", uint64(10774), typeinfo.Int64Type, false),
   343  					Theirs: newColTypeInfo("c6", uint64(10774), typeinfo.Int8Type, false),
   344  				},
   345  			},
   346  		},
   347  	},
   348  	{
   349  		name: "index definition collision",
   350  		setup: []testCommand{
   351  			{commands.SqlCmd{}, []string{"-q", "create index c3_idx on test(c3);"}},
   352  			{commands.AddCmd{}, []string{"."}},
   353  			{commands.CommitCmd{}, []string{"-m", "modified branch master"}},
   354  			{commands.CheckoutCmd{}, []string{"other"}},
   355  			{commands.SqlCmd{}, []string{"-q", "create index c3_index on test(c3);"}},
   356  			{commands.AddCmd{}, []string{"."}},
   357  			{commands.CommitCmd{}, []string{"-m", "modified branch other"}},
   358  			{commands.CheckoutCmd{}, []string{"master"}},
   359  		},
   360  		expConflict: merge.SchemaConflict{
   361  			TableName: "test",
   362  			IdxConflicts: []merge.IdxConflict{
   363  				{
   364  					Kind:   merge.TagCollision,
   365  					Ours:   schema.NewIndex("c3_idx", []uint64{4696}, []uint64{4696, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   366  					Theirs: schema.NewIndex("c3_index", []uint64{4696}, []uint64{4696, 3228}, nil, schema.IndexProperties{IsUserDefined: true}),
   367  				},
   368  			},
   369  		},
   370  	},
   371  }
   372  
   373  var setupForeignKeyTests = []testCommand{
   374  	{commands.SqlCmd{}, []string{"-q", "create table test (" +
   375  		"pk int not null primary key," +
   376  		"t1 int not null," +
   377  		"t2 int," +
   378  		"t3 int);"}},
   379  	{commands.SqlCmd{}, []string{"-q", "alter table test add index t1_idx (t1);"}},
   380  	{commands.SqlCmd{}, []string{"-q", "create table quiz (" +
   381  		"pk int not null primary key," +
   382  		"q1 int not null," +
   383  		"q2 int not null," +
   384  		"index q2_idx (q2)," +
   385  		"constraint q1_fk foreign key (q1) references test(t1));"}},
   386  	{commands.AddCmd{}, []string{"."}},
   387  	{commands.CommitCmd{}, []string{"-m", "setup common"}},
   388  	{commands.BranchCmd{}, []string{"other"}},
   389  }
   390  
   391  var mergeForeignKeyTests = []mergeForeignKeyTest{
   392  	{
   393  		name:  "no changes",
   394  		setup: []testCommand{},
   395  		fkColl: fkCollection(doltdb.ForeignKey{
   396  			Name:                   "q1_fk",
   397  			TableName:              "quiz",
   398  			TableIndex:             "q1",
   399  			TableColumns:           []uint64{13001},
   400  			ReferencedTableName:    "test",
   401  			ReferencedTableIndex:   "t1_idx",
   402  			ReferencedTableColumns: []uint64{12111}}),
   403  		expFKConflict: []merge.FKConflict{},
   404  	},
   405  	//{
   406  	//	name: "add foreign key, drop foreign key, merge",
   407  	//	setup: []testCommand{
   408  	//		{commands.SqlCmd{}, []string{"-q", "alter table quiz add constraint q2_fk foreign key (q2) references test(t2);"}},
   409  	//		{commands.AddCmd{}, []string{"."}},
   410  	//		{commands.CommitCmd{}, []string{"-m", "modified branch master"}},
   411  	//		{commands.CheckoutCmd{}, []string{"other"}},
   412  	//		{commands.SqlCmd{}, []string{"-q", "alter table quiz drop constraint q1_fk;"}},
   413  	//		{commands.AddCmd{}, []string{"."}},
   414  	//		{commands.CommitCmd{}, []string{"-m", "modified branch other"}},
   415  	//		{commands.CheckoutCmd{}, []string{"master"}},
   416  	//	},
   417  	//	fkColl: fkCollection(
   418  	//		&doltdb.ForeignKey{
   419  	//			Name:                   "q2_fk",
   420  	//			TableName:              "quiz",
   421  	//			TableIndex:             "dolt_fk_2",
   422  	//			TableColumns:           []uint64{12},
   423  	//			ReferencedTableName:    "test",
   424  	//			ReferencedTableIndex:   "dolt_fk_2",
   425  	//			ReferencedTableColumns: []uint64{2}}),
   426  	//	expFKConflict: []merge.FKConflict{},
   427  	//},
   428  }
   429  
   430  func colCollection(cols ...schema.Column) *schema.ColCollection {
   431  	return schema.NewColCollection(cols...)
   432  }
   433  
   434  // SchemaFromColsAndIdxs creates a Schema from a ColCollection and an IndexCollection.
   435  func schemaFromColsAndIdxs(allCols *schema.ColCollection, indexes ...schema.Index) schema.Schema {
   436  	sch := schema.MustSchemaFromCols(allCols)
   437  	sch.Indexes().AddIndex(indexes...)
   438  	return sch
   439  }
   440  
   441  func newColTypeInfo(name string, tag uint64, typeInfo typeinfo.TypeInfo, partOfPK bool, constraints ...schema.ColConstraint) schema.Column {
   442  	c, err := schema.NewColumnWithTypeInfo(name, tag, typeInfo, partOfPK, "", false, "", constraints...)
   443  	if err != nil {
   444  		panic("could not create column")
   445  	}
   446  	return c
   447  }
   448  
   449  func fkCollection(fks ...doltdb.ForeignKey) *doltdb.ForeignKeyCollection {
   450  	fkc, err := doltdb.NewForeignKeyCollection(fks...)
   451  	if err != nil {
   452  		panic(err)
   453  	}
   454  	return fkc
   455  }
   456  
   457  func testMergeSchemas(t *testing.T, test mergeSchemaTest) {
   458  	dEnv := dtestutils.CreateTestEnv()
   459  	ctx := context.Background()
   460  	for _, c := range setupCommon {
   461  		c.exec(t, ctx, dEnv)
   462  	}
   463  	for _, c := range test.setup {
   464  		c.exec(t, ctx, dEnv)
   465  	}
   466  
   467  	// assert that we're on master
   468  	exitCode := commands.CheckoutCmd{}.Exec(ctx, "checkout", []string{"master"}, dEnv)
   469  	require.Equal(t, 0, exitCode)
   470  
   471  	// merge branches
   472  	exitCode = commands.MergeCmd{}.Exec(ctx, "merge", []string{"other"}, dEnv)
   473  	assert.Equal(t, 0, exitCode)
   474  
   475  	wr, err := dEnv.WorkingRoot(ctx)
   476  	assert.NoError(t, err)
   477  	tbl, ok, err := wr.GetTable(ctx, "test")
   478  	assert.True(t, ok)
   479  	require.NoError(t, err)
   480  	sch, err := tbl.GetSchema(ctx)
   481  	require.NoError(t, err)
   482  
   483  	assert.Equal(t, test.sch.GetAllCols(), sch.GetAllCols())
   484  	assert.Equal(t, test.sch.Indexes(), sch.Indexes())
   485  }
   486  
   487  func testMergeSchemasWithConflicts(t *testing.T, test mergeSchemaConflictTest) {
   488  	getSchema := func(t *testing.T, dEnv *env.DoltEnv) schema.Schema {
   489  		ctx := context.Background()
   490  		wr, err := dEnv.WorkingRoot(ctx)
   491  		assert.NoError(t, err)
   492  		tbl, ok, err := wr.GetTable(ctx, "test")
   493  		assert.True(t, ok)
   494  		require.NoError(t, err)
   495  		sch, err := tbl.GetSchema(ctx)
   496  		require.NoError(t, err)
   497  		return sch
   498  	}
   499  
   500  	dEnv := dtestutils.CreateTestEnv()
   501  	ctx := context.Background()
   502  	for _, c := range setupCommon {
   503  		c.exec(t, ctx, dEnv)
   504  	}
   505  
   506  	ancSch := getSchema(t, dEnv)
   507  
   508  	for _, c := range test.setup {
   509  		c.exec(t, ctx, dEnv)
   510  	}
   511  
   512  	// assert that we're on master
   513  	exitCode := commands.CheckoutCmd{}.Exec(ctx, "checkout", []string{"master"}, dEnv)
   514  	require.Equal(t, 0, exitCode)
   515  
   516  	masterSch := getSchema(t, dEnv)
   517  
   518  	exitCode = commands.CheckoutCmd{}.Exec(ctx, "checkout", []string{"other"}, dEnv)
   519  	require.Equal(t, 0, exitCode)
   520  
   521  	otherSch := getSchema(t, dEnv)
   522  
   523  	_, actConflicts, err := merge.SchemaMerge(masterSch, otherSch, ancSch, "test")
   524  	require.NoError(t, err)
   525  	assert.Equal(t, actConflicts.TableName, "test")
   526  
   527  	assert.Equal(t, test.expConflict.Count(), actConflicts.Count())
   528  
   529  	require.Equal(t, len(test.expConflict.IdxConflicts), len(actConflicts.IdxConflicts))
   530  	for i, acc := range actConflicts.IdxConflicts {
   531  		assert.True(t, test.expConflict.IdxConflicts[i].Ours.Equals(acc.Ours))
   532  		assert.True(t, test.expConflict.IdxConflicts[i].Theirs.Equals(acc.Theirs))
   533  	}
   534  
   535  	require.Equal(t, len(test.expConflict.IdxConflicts), len(actConflicts.IdxConflicts))
   536  	for i, icc := range actConflicts.IdxConflicts {
   537  		assert.True(t, test.expConflict.IdxConflicts[i].Ours.Equals(icc.Ours))
   538  		assert.True(t, test.expConflict.IdxConflicts[i].Theirs.Equals(icc.Theirs))
   539  	}
   540  }
   541  
   542  func testMergeForeignKeys(t *testing.T, test mergeForeignKeyTest) {
   543  	dEnv := dtestutils.CreateTestEnv()
   544  	ctx := context.Background()
   545  	for _, c := range setupForeignKeyTests {
   546  		c.exec(t, ctx, dEnv)
   547  	}
   548  
   549  	ancRoot, err := dEnv.WorkingRoot(ctx)
   550  	require.NoError(t, err)
   551  
   552  	for _, c := range test.setup {
   553  		c.exec(t, ctx, dEnv)
   554  	}
   555  
   556  	// assert that we're on master
   557  	exitCode := commands.CheckoutCmd{}.Exec(ctx, "checkout", []string{"master"}, dEnv)
   558  	require.Equal(t, 0, exitCode)
   559  
   560  	masterRoot, err := dEnv.WorkingRoot(ctx)
   561  	require.NoError(t, err)
   562  
   563  	exitCode = commands.CheckoutCmd{}.Exec(ctx, "checkout", []string{"other"}, dEnv)
   564  	require.Equal(t, 0, exitCode)
   565  
   566  	otherRoot, err := dEnv.WorkingRoot(ctx)
   567  	require.NoError(t, err)
   568  
   569  	mergedRoot, _, err := merge.MergeRoots(ctx, masterRoot, otherRoot, ancRoot)
   570  	assert.NoError(t, err)
   571  
   572  	fkc, err := mergedRoot.GetForeignKeyCollection(ctx)
   573  	assert.NoError(t, err)
   574  	assert.Equal(t, test.fkColl.Count(), fkc.Count())
   575  
   576  	err = test.fkColl.Iter(func(expFK doltdb.ForeignKey) (stop bool, err error) {
   577  		actFK, ok := fkc.GetByTags(expFK.TableColumns, expFK.ReferencedTableColumns)
   578  		assert.True(t, ok)
   579  		assert.Equal(t, expFK, actFK)
   580  		return false, nil
   581  	})
   582  	assert.NoError(t, err)
   583  }