github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/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/dolthub/go-mysql-server/sql" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 25 "github.com/dolthub/dolt/go/cmd/dolt/cli" 26 "github.com/dolthub/dolt/go/cmd/dolt/commands" 27 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 28 "github.com/dolthub/dolt/go/libraries/doltcore/dtestutils" 29 "github.com/dolthub/dolt/go/libraries/doltcore/env" 30 "github.com/dolthub/dolt/go/libraries/doltcore/merge" 31 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 32 "github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo" 33 "github.com/dolthub/dolt/go/libraries/doltcore/table/editor" 34 "github.com/dolthub/dolt/go/store/types" 35 ) 36 37 type testCommand struct { 38 cmd cli.Command 39 args args 40 } 41 42 func (tc testCommand) exec(t *testing.T, ctx context.Context, dEnv *env.DoltEnv) int { 43 cliCtx, err := commands.NewArgFreeCliContext(ctx, dEnv) 44 require.NoError(t, err) 45 return tc.cmd.Exec(ctx, tc.cmd.Name(), tc.args, dEnv, cliCtx) 46 } 47 48 type args []string 49 50 // TestMergeSchemas are schema merge integration tests from 2020 51 func TestMergeSchemas(t *testing.T) { 52 for _, test := range mergeSchemaTests { 53 t.Run(test.name, func(t *testing.T) { 54 testMergeSchemas(t, test) 55 }) 56 } 57 for _, test := range mergeSchemaConflictTests { 58 t.Run(test.name, func(t *testing.T) { 59 testMergeSchemasWithConflicts(t, test) 60 }) 61 } 62 for _, test := range mergeForeignKeyTests { 63 t.Run(test.name, func(t *testing.T) { 64 testMergeForeignKeys(t, test) 65 }) 66 } 67 } 68 69 type mergeSchemaTest struct { 70 name string 71 setup []testCommand 72 sch schema.Schema 73 skip bool 74 } 75 76 type mergeSchemaConflictTest struct { 77 name string 78 setup []testCommand 79 expConflict merge.SchemaConflict 80 expectedErr error 81 } 82 83 type mergeForeignKeyTest struct { 84 name string 85 setup []testCommand 86 fkColl *doltdb.ForeignKeyCollection 87 expFKConflict []merge.FKConflict 88 } 89 90 var setupCommon = []testCommand{ 91 {commands.SqlCmd{}, []string{"-q", "create table test (" + 92 "pk int not null primary key," + 93 "c1 int not null," + 94 "c2 int," + 95 "c3 int);"}}, 96 {commands.SqlCmd{}, []string{"-q", "create index c1_idx on test(c1)"}}, 97 {commands.AddCmd{}, []string{"."}}, 98 {commands.CommitCmd{}, []string{"-m", "setup common"}}, 99 {commands.BranchCmd{}, []string{"other"}}, 100 } 101 102 var mergeSchemaTests = []mergeSchemaTest{ 103 { 104 name: "no changes", 105 setup: []testCommand{}, 106 sch: schemaFromColsAndIdxs( 107 colCollection( 108 newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}), 109 newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}), 110 newColTypeInfo("c2", uint64(8539), typeinfo.Int32Type, false), 111 newColTypeInfo("c3", uint64(4696), typeinfo.Int32Type, false)), 112 schema.NewIndex("c1_idx", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 113 ), 114 }, 115 { 116 name: "add cols, drop cols, merge", 117 setup: []testCommand{ 118 {commands.SqlCmd{}, []string{"-q", "alter table test drop column c2;"}}, 119 {commands.SqlCmd{}, []string{"-q", "alter table test add column c8 int;"}}, 120 {commands.AddCmd{}, []string{"."}}, 121 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 122 {commands.CheckoutCmd{}, []string{"other"}}, 123 {commands.SqlCmd{}, []string{"-q", "alter table test drop column c3;"}}, 124 {commands.SqlCmd{}, []string{"-q", "alter table test add column c9 int;"}}, 125 {commands.AddCmd{}, []string{"."}}, 126 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 127 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 128 }, 129 sch: schemaFromColsAndIdxs( 130 colCollection( 131 newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}), 132 newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}), 133 newColTypeInfo("c8", uint64(12393), typeinfo.Int32Type, false), 134 newColTypeInfo("c9", uint64(4508), typeinfo.Int32Type, false)), 135 schema.NewIndex("c1_idx", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 136 ), 137 }, 138 { 139 name: "add constraint, drop constraint, merge", 140 setup: []testCommand{ 141 {commands.SqlCmd{}, []string{"-q", "alter table test modify c1 int null;"}}, 142 {commands.AddCmd{}, []string{"."}}, 143 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 144 {commands.CheckoutCmd{}, []string{"other"}}, 145 {commands.SqlCmd{}, []string{"-q", "alter table test modify c2 int not null;"}}, 146 {commands.AddCmd{}, []string{"."}}, 147 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 148 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 149 }, 150 sch: schemaFromColsAndIdxs( 151 colCollection( 152 newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}), 153 newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false), 154 newColTypeInfo("c2", uint64(8539), typeinfo.Int32Type, false, schema.NotNullConstraint{}), 155 newColTypeInfo("c3", uint64(4696), typeinfo.Int32Type, false)), 156 schema.NewIndex("c1_idx", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 157 ), 158 }, 159 { 160 name: "add index, drop index, merge", 161 setup: []testCommand{ 162 {commands.SqlCmd{}, []string{"-q", "create index c3_idx on test(c3);"}}, 163 {commands.AddCmd{}, []string{"."}}, 164 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 165 {commands.CheckoutCmd{}, []string{"other"}}, 166 {commands.SqlCmd{}, []string{"-q", "alter table test drop index c1_idx;"}}, 167 {commands.AddCmd{}, []string{"."}}, 168 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 169 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 170 }, 171 sch: schemaFromColsAndIdxs( 172 colCollection( 173 newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}), 174 newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}), 175 newColTypeInfo("c2", uint64(8539), typeinfo.Int32Type, false), 176 newColTypeInfo("c3", uint64(4696), typeinfo.Int32Type, false)), 177 schema.NewIndex("c3_idx", []uint64{4696}, []uint64{4696, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 178 ), 179 }, 180 { 181 name: "rename columns", 182 setup: []testCommand{ 183 {commands.SqlCmd{}, []string{"-q", "alter table test rename column c3 to c33;"}}, 184 {commands.AddCmd{}, []string{"."}}, 185 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 186 {commands.CheckoutCmd{}, []string{"other"}}, 187 {commands.SqlCmd{}, []string{"-q", "alter table test rename column c2 to c22;"}}, 188 {commands.AddCmd{}, []string{"."}}, 189 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 190 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 191 }, 192 // hmmm, we created new columns with a rename? 193 sch: schemaFromColsAndIdxs( 194 colCollection( 195 newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}), 196 newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}), 197 newColTypeInfo("c22", uint64(8539), typeinfo.Int32Type, false), 198 newColTypeInfo("c33", uint64(4696), typeinfo.Int32Type, false)), 199 schema.NewIndex("c1_idx", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 200 ), 201 }, 202 { 203 name: "rename indexes", 204 setup: []testCommand{ 205 {commands.SqlCmd{}, []string{"-q", "alter table test drop index c1_idx;"}}, 206 {commands.SqlCmd{}, []string{"-q", "create index c1_index on test(c1);"}}, 207 {commands.AddCmd{}, []string{"."}}, 208 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 209 }, 210 sch: schemaFromColsAndIdxs( 211 colCollection( 212 newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}), 213 newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}), 214 newColTypeInfo("c2", uint64(8539), typeinfo.Int32Type, false), 215 newColTypeInfo("c3", uint64(4696), typeinfo.Int32Type, false)), 216 schema.NewIndex("c1_index", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 217 ), 218 }, 219 { 220 name: "add same column on both branches, merge", 221 setup: []testCommand{ 222 {commands.SqlCmd{}, []string{"-q", "alter table test add column c4 int;"}}, 223 {commands.AddCmd{}, []string{"."}}, 224 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 225 {commands.CheckoutCmd{}, []string{"other"}}, 226 {commands.SqlCmd{}, []string{"-q", "alter table test add column c4 int;"}}, 227 {commands.AddCmd{}, []string{"."}}, 228 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 229 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 230 }, 231 sch: schemaFromColsAndIdxs( 232 colCollection( 233 newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}), 234 newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}), 235 newColTypeInfo("c2", uint64(8539), typeinfo.Int32Type, false), 236 newColTypeInfo("c3", uint64(4696), typeinfo.Int32Type, false), 237 newColTypeInfo("c4", uint64(1716), typeinfo.Int32Type, false)), 238 schema.NewIndex("c1_idx", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 239 ), 240 }, 241 { 242 name: "add same index on both branches, merge", 243 setup: []testCommand{ 244 {commands.SqlCmd{}, []string{"-q", "create index c3_idx on test(c3);"}}, 245 {commands.AddCmd{}, []string{"."}}, 246 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 247 {commands.CheckoutCmd{}, []string{"other"}}, 248 {commands.SqlCmd{}, []string{"-q", "create index c3_idx on test(c3);"}}, 249 {commands.AddCmd{}, []string{"."}}, 250 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 251 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 252 }, 253 sch: schemaFromColsAndIdxs( 254 colCollection( 255 newColTypeInfo("pk", uint64(3228), typeinfo.Int32Type, true, schema.NotNullConstraint{}), 256 newColTypeInfo("c1", uint64(8201), typeinfo.Int32Type, false, schema.NotNullConstraint{}), 257 newColTypeInfo("c2", uint64(8539), typeinfo.Int32Type, false), 258 newColTypeInfo("c3", uint64(4696), typeinfo.Int32Type, false)), 259 schema.NewIndex("c1_idx", []uint64{8201}, []uint64{8201, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 260 schema.NewIndex("c3_idx", []uint64{4696}, []uint64{4696, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 261 ), 262 }, 263 } 264 265 var mergeSchemaConflictTests = []mergeSchemaConflictTest{ 266 { 267 name: "no conflicts", 268 expConflict: merge.SchemaConflict{ 269 TableName: "test", 270 }, 271 }, 272 { 273 name: "column name collisions", 274 setup: []testCommand{ 275 {commands.SqlCmd{}, []string{"-q", "alter table test rename column c3 to c4;"}}, 276 {commands.SqlCmd{}, []string{"-q", "alter table test add column C6 int;"}}, 277 {commands.AddCmd{}, []string{"."}}, 278 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 279 {commands.CheckoutCmd{}, []string{"other"}}, 280 {commands.SqlCmd{}, []string{"-q", "alter table test rename column c2 to c4;"}}, 281 {commands.SqlCmd{}, []string{"-q", "alter table test add column c6 int;"}}, 282 {commands.AddCmd{}, []string{"."}}, 283 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 284 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 285 }, 286 expConflict: merge.SchemaConflict{ 287 TableName: "test", 288 ColConflicts: []merge.ColConflict{ 289 { 290 Kind: merge.NameCollision, 291 Ours: newColTypeInfo("C6", uint64(13258), typeinfo.Int32Type, false), 292 Theirs: newColTypeInfo("c6", uint64(13258), typeinfo.Int32Type, false), 293 }, 294 { 295 Kind: merge.NameCollision, 296 Ours: newColTypeInfo("c4", uint64(4696), typeinfo.Int32Type, false), 297 Theirs: newColTypeInfo("c4", uint64(8539), typeinfo.Int32Type, false), 298 }, 299 }, 300 }, 301 }, 302 { 303 name: "index name collisions", 304 setup: []testCommand{ 305 {commands.SqlCmd{}, []string{"-q", "create index `both` on test (c1,c2);"}}, 306 {commands.AddCmd{}, []string{"."}}, 307 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 308 {commands.CheckoutCmd{}, []string{"other"}}, 309 {commands.SqlCmd{}, []string{"-q", "create index `both` on test (c2, c3);"}}, 310 {commands.AddCmd{}, []string{"."}}, 311 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 312 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 313 }, 314 expConflict: merge.SchemaConflict{ 315 TableName: "test", 316 IdxConflicts: []merge.IdxConflict{ 317 { 318 Kind: merge.NameCollision, 319 Ours: schema.NewIndex("both", []uint64{8201, 8539}, []uint64{8201, 8539, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 320 Theirs: schema.NewIndex("both", []uint64{8539, 4696}, []uint64{8539, 4696, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 321 }, 322 }, 323 }, 324 }, 325 { 326 name: "column definition collision", 327 setup: []testCommand{ 328 {commands.SqlCmd{}, []string{"-q", "alter table test add column c40 int;"}}, 329 {commands.SqlCmd{}, []string{"-q", "alter table test add column c6 bigint;"}}, 330 {commands.AddCmd{}, []string{"."}}, 331 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 332 {commands.CheckoutCmd{}, []string{"other"}}, 333 {commands.SqlCmd{}, []string{"-q", "alter table test add column c40 int;"}}, 334 {commands.SqlCmd{}, []string{"-q", "alter table test rename column c40 to c44;"}}, 335 {commands.SqlCmd{}, []string{"-q", "alter table test add column c6 tinyint;"}}, 336 {commands.AddCmd{}, []string{"."}}, 337 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 338 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 339 }, 340 expConflict: merge.SchemaConflict{ 341 TableName: "test", 342 ColConflicts: []merge.ColConflict{ 343 { 344 Kind: merge.TagCollision, 345 Ours: newColTypeInfo("c40", uint64(679), typeinfo.Int32Type, false), 346 Theirs: newColTypeInfo("c44", uint64(679), typeinfo.Int32Type, false), 347 }, 348 { 349 Kind: merge.TagCollision, 350 Ours: newColTypeInfo("c6", uint64(10774), typeinfo.Int64Type, false), 351 Theirs: newColTypeInfo("c6", uint64(10774), typeinfo.Int8Type, false), 352 }, 353 }, 354 }, 355 }, 356 { 357 name: "index definition collision", 358 setup: []testCommand{ 359 {commands.SqlCmd{}, []string{"-q", "create index c3_idx on test(c3);"}}, 360 {commands.AddCmd{}, []string{"."}}, 361 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 362 {commands.CheckoutCmd{}, []string{"other"}}, 363 {commands.SqlCmd{}, []string{"-q", "create index c3_index on test(c3);"}}, 364 {commands.AddCmd{}, []string{"."}}, 365 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 366 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 367 }, 368 expConflict: merge.SchemaConflict{ 369 TableName: "test", 370 IdxConflicts: []merge.IdxConflict{ 371 { 372 Kind: merge.TagCollision, 373 Ours: schema.NewIndex("c3_idx", []uint64{4696}, []uint64{4696, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 374 Theirs: schema.NewIndex("c3_index", []uint64{4696}, []uint64{4696, 3228}, nil, schema.IndexProperties{IsUserDefined: true}), 375 }, 376 }, 377 }, 378 }, 379 { 380 name: "check definition collision", 381 setup: []testCommand{ 382 {commands.SqlCmd{}, []string{"-q", "alter table test add constraint chk check (c3 > 0);"}}, 383 {commands.AddCmd{}, []string{"."}}, 384 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 385 {commands.CheckoutCmd{}, []string{"other"}}, 386 {commands.SqlCmd{}, []string{"-q", "alter table test add constraint chk check (c3 < 0);"}}, 387 {commands.AddCmd{}, []string{"."}}, 388 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 389 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 390 }, 391 expConflict: merge.SchemaConflict{ 392 TableName: "test", 393 ChkConflicts: []merge.ChkConflict{ 394 { 395 Kind: merge.TagCollision, 396 Ours: schema.NewCheck("chk", "(c3 > 0)", true), 397 Theirs: schema.NewCheck("chk", "(c3 < 0)", true), 398 }, 399 { 400 Kind: merge.NameCollision, 401 Ours: schema.NewCheck("chk", "(c3 > 0)", true), 402 Theirs: schema.NewCheck("chk", "(c3 < 0)", true), 403 }, 404 }, 405 }, 406 }, 407 { 408 name: "modified check", 409 setup: []testCommand{ 410 {commands.SqlCmd{}, []string{"-q", "alter table test add constraint chk check (c3 > 0);"}}, 411 {commands.AddCmd{}, []string{"."}}, 412 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 413 414 {commands.CheckoutCmd{}, []string{"other"}}, 415 {commands.SqlCmd{}, []string{"-q", "alter table test add constraint chk check (c3 > 0);"}}, 416 {commands.AddCmd{}, []string{"."}}, 417 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 418 419 {commands.MergeCmd{}, []string{env.DefaultInitBranch}}, 420 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 421 {commands.MergeCmd{}, []string{"other"}}, 422 423 {commands.SqlCmd{}, []string{"-q", "alter table test drop constraint chk;"}}, 424 {commands.SqlCmd{}, []string{"-q", "alter table test add constraint chk check (c3 > 10);"}}, 425 {commands.AddCmd{}, []string{"."}}, 426 {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 427 428 {commands.CheckoutCmd{}, []string{"other"}}, 429 {commands.SqlCmd{}, []string{"-q", "alter table test drop constraint chk;"}}, 430 {commands.SqlCmd{}, []string{"-q", "alter table test add constraint chk check (c3 < 10);"}}, 431 {commands.AddCmd{}, []string{"."}}, 432 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 433 }, 434 expConflict: merge.SchemaConflict{ 435 TableName: "test", 436 ChkConflicts: []merge.ChkConflict{ 437 { 438 Kind: merge.TagCollision, 439 Ours: schema.NewCheck("chk", "(c3 > 10)", true), 440 Theirs: schema.NewCheck("chk", "(c3 < 10)", true), 441 }, 442 { 443 Kind: merge.NameCollision, 444 Ours: schema.NewCheck("chk", "(c3 > 10)", true), 445 Theirs: schema.NewCheck("chk", "(c3 < 10)", true), 446 }, 447 }, 448 }, 449 }, 450 { 451 name: "primary key conflicts", 452 setup: []testCommand{ 453 {commands.CheckoutCmd{}, []string{"other"}}, 454 {commands.SqlCmd{}, []string{"-q", "alter table test drop primary key;"}}, 455 {commands.AddCmd{}, []string{"."}}, 456 {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 457 {commands.CheckoutCmd{}, []string{env.DefaultInitBranch}}, 458 }, 459 expectedErr: merge.ErrMergeWithDifferentPks.New("test"), 460 }, 461 } 462 463 var setupForeignKeyTests = []testCommand{ 464 {commands.SqlCmd{}, []string{"-q", "create table test (" + 465 "pk int not null primary key," + 466 "t1 int not null," + 467 "t2 int," + 468 "t3 int);"}}, 469 {commands.SqlCmd{}, []string{"-q", "alter table test add index t1_idx (t1);"}}, 470 {commands.SqlCmd{}, []string{"-q", "create table quiz (" + 471 "pk int not null primary key," + 472 "q1 int not null," + 473 "q2 int not null," + 474 "index q2_idx (q2)," + 475 "constraint q1_fk foreign key (q1) references test(t1));"}}, 476 {commands.AddCmd{}, []string{"."}}, 477 {commands.CommitCmd{}, []string{"-m", "setup common"}}, 478 {commands.BranchCmd{}, []string{"other"}}, 479 } 480 481 var mergeForeignKeyTests = []mergeForeignKeyTest{ 482 { 483 name: "no changes", 484 setup: []testCommand{}, 485 fkColl: fkCollection(doltdb.ForeignKey{ 486 Name: "q1_fk", 487 TableName: "quiz", 488 TableIndex: "q1", 489 TableColumns: []uint64{13001}, 490 ReferencedTableName: "test", 491 ReferencedTableIndex: "t1_idx", 492 ReferencedTableColumns: []uint64{12111}, 493 UnresolvedFKDetails: doltdb.UnresolvedFKDetails{ 494 TableColumns: []string{"q1"}, 495 ReferencedTableColumns: []string{"t1"}, 496 }, 497 }), 498 expFKConflict: []merge.FKConflict{}, 499 }, 500 //{ 501 // name: "add foreign key, drop foreign key, merge", 502 // setup: []testCommand{ 503 // {commands.SqlCmd{}, []string{"-q", "alter table quiz add constraint q2_fk foreign key (q2) references test(t2);"}}, 504 // {commands.AddCmd{}, []string{"."}}, 505 // {commands.CommitCmd{}, []string{"-m", "modified branch main"}}, 506 // {commands.CheckoutCmd{}, []string{"other"}}, 507 // {commands.SqlCmd{}, []string{"-q", "alter table quiz drop constraint q1_fk;"}}, 508 // {commands.AddCmd{}, []string{"."}}, 509 // {commands.CommitCmd{}, []string{"-m", "modified branch other"}}, 510 // {commands.CheckoutCmd{}, []string{"main"}}, 511 // }, 512 // fkColl: fkCollection( 513 // &doltdb.ForeignKey{ 514 // Name: "q2_fk", 515 // TableName: "quiz", 516 // TableIndex: "dolt_fk_2", 517 // TableColumns: []uint64{12}, 518 // ReferencedTableName: "test", 519 // ReferencedTableIndex: "dolt_fk_2", 520 // ReferencedTableColumns: []uint64{2}}), 521 // expFKConflict: []merge.FKConflict{}, 522 //}, 523 } 524 525 func colCollection(cols ...schema.Column) *schema.ColCollection { 526 return schema.NewColCollection(cols...) 527 } 528 529 // SchemaFromColsAndIdxs creates a Schema from a ColCollection and an IndexCollection. 530 func schemaFromColsAndIdxs(allCols *schema.ColCollection, indexes ...schema.Index) schema.Schema { 531 sch := schema.MustSchemaFromCols(allCols) 532 sch.Indexes().AddIndex(indexes...) 533 return sch 534 } 535 536 func newColTypeInfo(name string, tag uint64, typeInfo typeinfo.TypeInfo, partOfPK bool, constraints ...schema.ColConstraint) schema.Column { 537 c, err := schema.NewColumnWithTypeInfo(name, tag, typeInfo, partOfPK, "", false, "", constraints...) 538 if err != nil { 539 panic("could not create column") 540 } 541 return c 542 } 543 544 func fkCollection(fks ...doltdb.ForeignKey) *doltdb.ForeignKeyCollection { 545 fkc, err := doltdb.NewForeignKeyCollection(fks...) 546 if err != nil { 547 panic(err) 548 } 549 return fkc 550 } 551 552 func testMergeSchemas(t *testing.T, test mergeSchemaTest) { 553 if test.skip { 554 t.Skip() 555 return 556 } 557 558 dEnv := dtestutils.CreateTestEnv() 559 defer dEnv.DoltDB.Close() 560 ctx := context.Background() 561 562 cliCtx, _ := commands.NewArgFreeCliContext(ctx, dEnv) 563 564 for _, c := range setupCommon { 565 exit := c.exec(t, ctx, dEnv) 566 require.Equal(t, 0, exit) 567 } 568 for _, c := range test.setup { 569 exit := c.exec(t, ctx, dEnv) 570 require.Equal(t, 0, exit) 571 } 572 573 // assert that we're on main 574 exitCode := commands.CheckoutCmd{}.Exec(ctx, "checkout", []string{env.DefaultInitBranch}, dEnv, cliCtx) 575 require.Equal(t, 0, exitCode) 576 577 // merge branches 578 exitCode = commands.MergeCmd{}.Exec(ctx, "merge", []string{"other"}, dEnv, cliCtx) 579 assert.Equal(t, 0, exitCode) 580 581 wr, err := dEnv.WorkingRoot(ctx) 582 assert.NoError(t, err) 583 tbl, ok, err := wr.GetTable(ctx, doltdb.TableName{Name: "test"}) 584 assert.True(t, ok) 585 require.NoError(t, err) 586 sch, err := tbl.GetSchema(ctx) 587 require.NoError(t, err) 588 589 assert.Equal(t, test.sch.GetAllCols(), sch.GetAllCols()) 590 assert.Equal(t, test.sch.Indexes(), sch.Indexes()) 591 } 592 593 func testMergeSchemasWithConflicts(t *testing.T, test mergeSchemaConflictTest) { 594 getSchema := func(t *testing.T, dEnv *env.DoltEnv) schema.Schema { 595 ctx := context.Background() 596 wr, err := dEnv.WorkingRoot(ctx) 597 assert.NoError(t, err) 598 tbl, ok, err := wr.GetTable(ctx, doltdb.TableName{Name: "test"}) 599 assert.True(t, ok) 600 require.NoError(t, err) 601 sch, err := tbl.GetSchema(ctx) 602 require.NoError(t, err) 603 return sch 604 } 605 606 dEnv := dtestutils.CreateTestEnv() 607 defer dEnv.DoltDB.Close() 608 ctx := context.Background() 609 for _, c := range setupCommon { 610 exit := c.exec(t, ctx, dEnv) 611 require.Equal(t, 0, exit) 612 } 613 614 ancSch := getSchema(t, dEnv) 615 616 for _, c := range test.setup { 617 exit := c.exec(t, ctx, dEnv) 618 require.Equal(t, 0, exit) 619 } 620 621 cliCtx, _ := commands.NewArgFreeCliContext(ctx, dEnv) 622 623 // assert that we're on main 624 exitCode := commands.CheckoutCmd{}.Exec(ctx, "checkout", []string{env.DefaultInitBranch}, dEnv, cliCtx) 625 require.Equal(t, 0, exitCode) 626 627 mainSch := getSchema(t, dEnv) 628 629 exitCode = commands.CheckoutCmd{}.Exec(ctx, "checkout", []string{"other"}, dEnv, cliCtx) 630 require.Equal(t, 0, exitCode) 631 632 otherSch := getSchema(t, dEnv) 633 634 _, actConflicts, mergeInfo, _, err := merge.SchemaMerge(context.Background(), types.Format_Default, mainSch, otherSch, ancSch, "test") 635 assert.False(t, mergeInfo.InvalidateSecondaryIndexes) 636 if test.expectedErr != nil { 637 // We don't use errors.Is here because errors generated by `Kind.New` compare stack traces in their `Is` implementation. 638 assert.Equal(t, err.Error(), test.expectedErr.Error(), "Expected error '%s', instead got '%s'", test.expectedErr.Error(), err.Error()) 639 return 640 } 641 642 require.NoError(t, err) 643 assert.Equal(t, actConflicts.TableName, "test") 644 645 assert.Equal(t, test.expConflict.Count(), actConflicts.Count()) 646 647 require.Equal(t, len(test.expConflict.IdxConflicts), len(actConflicts.IdxConflicts)) 648 for i, acc := range actConflicts.IdxConflicts { 649 assert.True(t, test.expConflict.IdxConflicts[i].Ours.Equals(acc.Ours)) 650 assert.True(t, test.expConflict.IdxConflicts[i].Theirs.Equals(acc.Theirs)) 651 } 652 653 require.Equal(t, len(test.expConflict.ColConflicts), len(actConflicts.ColConflicts)) 654 for i, icc := range actConflicts.ColConflicts { 655 assert.True(t, test.expConflict.ColConflicts[i].Ours.Equals(icc.Ours)) 656 assert.True(t, test.expConflict.ColConflicts[i].Theirs.Equals(icc.Theirs)) 657 } 658 659 require.Equal(t, len(test.expConflict.ChkConflicts), len(actConflicts.ChkConflicts)) 660 for i, icc := range actConflicts.ChkConflicts { 661 assert.True(t, test.expConflict.ChkConflicts[i].Ours == icc.Ours) 662 assert.True(t, test.expConflict.ChkConflicts[i].Theirs == icc.Theirs) 663 } 664 } 665 666 func testMergeForeignKeys(t *testing.T, test mergeForeignKeyTest) { 667 dEnv := dtestutils.CreateTestEnv() 668 defer dEnv.DoltDB.Close() 669 ctx := context.Background() 670 for _, c := range setupForeignKeyTests { 671 exit := c.exec(t, ctx, dEnv) 672 require.Equal(t, 0, exit) 673 } 674 675 ancRoot, err := dEnv.WorkingRoot(ctx) 676 require.NoError(t, err) 677 678 for _, c := range test.setup { 679 exit := c.exec(t, ctx, dEnv) 680 require.Equal(t, 0, exit) 681 } 682 683 cliCtx, _ := commands.NewArgFreeCliContext(ctx, dEnv) 684 685 // assert that we're on main 686 exitCode := commands.CheckoutCmd{}.Exec(ctx, "checkout", []string{env.DefaultInitBranch}, dEnv, cliCtx) 687 require.Equal(t, 0, exitCode) 688 689 mainWS, err := dEnv.WorkingSet(ctx) 690 require.NoError(t, err) 691 mainRoot := mainWS.WorkingRoot() 692 693 exitCode = commands.CheckoutCmd{}.Exec(ctx, "checkout", []string{"other"}, dEnv, cliCtx) 694 require.Equal(t, 0, exitCode) 695 696 otherWS, err := dEnv.WorkingSet(ctx) 697 require.NoError(t, err) 698 otherRoot := otherWS.WorkingRoot() 699 700 opts := editor.TestEditorOptions(dEnv.DoltDB.ValueReadWriter()) 701 mo := merge.MergeOpts{IsCherryPick: false} 702 result, err := merge.MergeRoots(sql.NewContext(ctx), mainRoot, otherRoot, ancRoot, mainWS, otherWS, opts, mo) 703 assert.NoError(t, err) 704 705 fkc, err := result.Root.GetForeignKeyCollection(ctx) 706 assert.NoError(t, err) 707 assert.Equal(t, test.fkColl.Count(), fkc.Count()) 708 709 err = test.fkColl.Iter(func(expFK doltdb.ForeignKey) (stop bool, err error) { 710 actFK, ok := fkc.GetByTags(expFK.TableColumns, expFK.ReferencedTableColumns) 711 assert.True(t, ok) 712 assert.Equal(t, expFK, actFK) 713 return false, nil 714 }) 715 assert.NoError(t, err) 716 }