github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/merge/schema_merge_test.go (about) 1 // Copyright 2023 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/shopspring/decimal" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 26 "github.com/dolthub/dolt/go/cmd/dolt/commands/engine" 27 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 28 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable" 29 "github.com/dolthub/dolt/go/libraries/doltcore/dtestutils" 30 "github.com/dolthub/dolt/go/libraries/doltcore/merge" 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/sqle/dsess" 34 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil" 35 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/writer" 36 "github.com/dolthub/dolt/go/libraries/doltcore/table/editor" 37 "github.com/dolthub/dolt/go/store/hash" 38 "github.com/dolthub/dolt/go/store/types" 39 ) 40 41 type schemaMergeTest struct { 42 name string 43 ancestor table 44 left, right *table 45 merged table 46 conflict bool 47 skipNewFmt bool 48 skipOldFmt bool 49 skipFlipOnNewFormat bool 50 skipFlipOnOldFormat bool 51 dataTests []dataTest 52 } 53 54 type dataTest struct { 55 name string 56 ancestor []sql.Row 57 left, right []sql.Row 58 merged []sql.Row 59 constraintViolations []constraintViolation 60 dataConflict bool 61 skip bool 62 skipFlip bool 63 } 64 65 type table struct { 66 ns namedSchema 67 rows []sql.Row 68 } 69 70 type namedSchema struct { 71 name string 72 sch schema.Schema 73 create string 74 } 75 76 // TestSchemaMerge are schema merge integration tests from 2023 77 func TestSchemaMerge(t *testing.T) { 78 t.Run("column add/drop tests", func(t *testing.T) { 79 testSchemaMerge(t, columnAddDropTests) 80 }) 81 t.Run("column default tests", func(t *testing.T) { 82 testSchemaMerge(t, columnDefaultTests) 83 }) 84 t.Run("collation tests", func(t *testing.T) { 85 testSchemaMerge(t, collationTests) 86 }) 87 t.Run("nullability tests", func(t *testing.T) { 88 testSchemaMerge(t, nullabilityTests) 89 }) 90 t.Run("column type change tests", func(t *testing.T) { 91 testSchemaMerge(t, typeChangeTests) 92 }) 93 t.Run("column reordering tests", func(t *testing.T) { 94 testSchemaMerge(t, columnReorderingTests) 95 }) 96 t.Run("primary key change tests", func(t *testing.T) { 97 testSchemaMerge(t, keyChangeTests) 98 }) 99 t.Run("secondary index tests", func(t *testing.T) { 100 testSchemaMerge(t, secondaryIndexTests) 101 }) 102 t.Run("simple conflict tests", func(t *testing.T) { 103 testSchemaMerge(t, simpleConflictTests) 104 }) 105 t.Run("json merge tests", func(t *testing.T) { 106 testSchemaMerge(t, jsonMergeTests) 107 }) 108 } 109 110 var columnAddDropTests = []schemaMergeTest{ 111 { 112 name: "no schema changes", 113 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)"), row(1)), 114 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY)"), row(1)), 115 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY)"), row(1)), 116 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY)"), row(1)), 117 }, 118 // one side changes columns 119 { 120 name: "left side column add", 121 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) ")), 122 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)")), 123 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY) ")), 124 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)")), 125 dataTests: []dataTest{ 126 { 127 name: "left side adds column and assigns non-null value", 128 ancestor: singleRow(1), 129 left: singleRow(1, 2), 130 right: singleRow(1), 131 merged: singleRow(1, 2), 132 }, 133 { 134 name: "left side adds column and assigns null value", 135 ancestor: singleRow(1), 136 left: singleRow(1, nil), 137 right: singleRow(1), 138 merged: singleRow(1, nil), 139 }, 140 }, 141 }, 142 { 143 name: "left side column add with additional column after", 144 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) ")), 145 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)")), 146 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) ")), 147 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)")), 148 dataTests: []dataTest{ 149 { 150 name: "left side adds column and assigns non-null value, extra column is non-NULL", 151 ancestor: singleRow(1, 3), 152 left: singleRow(1, 2, 3), 153 right: singleRow(1, 3), 154 merged: singleRow(1, 2, 3), 155 }, 156 { 157 name: "left side adds column and assigns null value, extra column is non-NULL", 158 ancestor: singleRow(1, 3), 159 left: singleRow(1, nil, 3), 160 right: singleRow(1, 3), 161 merged: singleRow(1, nil, 3), 162 }, 163 { 164 // Skipped because of (https://github.com/dolthub/dolt/issues/6745) 165 name: "left side adds column and assigns non-null value, extra column has data change on right", 166 ancestor: singleRow(1, 3), 167 left: singleRow(1, 2, 3), 168 right: singleRow(1, 4), 169 merged: singleRow(1, 2, 4), 170 skipFlip: true, 171 }, 172 { 173 // Skipped because of (https://github.com/dolthub/dolt/issues/6745) 174 name: "left side adds column and assigns non-null value, extra column has data change on right to NULL", 175 ancestor: singleRow(1, 3), 176 left: singleRow(1, 2, 3), 177 right: singleRow(1, nil), 178 merged: singleRow(1, 2, nil), 179 skipFlip: true, 180 }, 181 { 182 // Skipped because of (https://github.com/dolthub/dolt/issues/6745) 183 name: "left side adds column and assigns non-null value, extra column has data change on right to non-NULL", 184 ancestor: singleRow(1, nil), 185 left: singleRow(1, 2, nil), 186 right: singleRow(1, 3), 187 merged: singleRow(1, 2, 3), 188 skipFlip: true, 189 }, 190 { 191 name: "left side adds column and assigns non-null value, extra column is NULL", 192 ancestor: singleRow(1, nil), 193 left: singleRow(1, 2, nil), 194 right: singleRow(1, nil), 195 merged: singleRow(1, 2, nil), 196 }, 197 { 198 name: "left side adds column and assigns null value, extra column is NULL", 199 ancestor: singleRow(1, nil), 200 left: singleRow(1, nil, nil), 201 right: singleRow(1, nil), 202 merged: singleRow(1, nil, nil), 203 }, 204 { 205 // Skipped because of (https://github.com/dolthub/dolt/issues/6745) 206 name: "left side adds column and assigns null value, extra column has data change on right", 207 ancestor: singleRow(1, 3), 208 left: singleRow(1, nil, 3), 209 right: singleRow(1, 4), 210 merged: singleRow(1, nil, 4), 211 skipFlip: true, 212 }, 213 { 214 // Skipped because of (https://github.com/dolthub/dolt/issues/6745) 215 name: "left side adds column and assigns null value, extra column has data change on right to NULL", 216 ancestor: singleRow(1, 3), 217 left: singleRow(1, nil, 3), 218 right: singleRow(1, nil), 219 merged: singleRow(1, nil, nil), 220 skipFlip: true, 221 }, 222 { 223 // Skipped because of (https://github.com/dolthub/dolt/issues/6745) 224 name: "left side adds column and assigns null value, extra column has data change on right to non-NULL", 225 ancestor: singleRow(1, nil), 226 left: singleRow(1, nil, nil), 227 right: singleRow(1, 3), 228 merged: singleRow(1, nil, 3), 229 skipFlip: true, 230 }, 231 }, 232 }, 233 { 234 name: "left side column drop", 235 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")), 236 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) ")), 237 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")), 238 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) ")), 239 dataTests: []dataTest{ 240 { 241 name: "no data change", 242 ancestor: singleRow(1, 2, 3), 243 left: singleRow(1, 2), 244 right: singleRow(1, 2, 3), 245 merged: singleRow(1, 2), 246 }, 247 { 248 name: "one side sets to NULL, other drops non-NULL", 249 ancestor: singleRow(1, 2, 3), 250 left: singleRow(1, 2), 251 right: singleRow(1, 2, nil), 252 dataConflict: true, 253 skip: true, 254 }, 255 { 256 name: "one side sets to NULL, other drops non-NULL, plus data change", 257 ancestor: singleRow(1, 2, 3), 258 left: singleRow(1, 2), 259 right: singleRow(1, 3, nil), 260 dataConflict: true, 261 }, 262 { 263 name: "one side sets to non-NULL, other drops NULL", 264 ancestor: singleRow(1, 2, nil), 265 left: singleRow(1, 2), 266 right: singleRow(1, 2, 3), 267 dataConflict: true, 268 }, 269 { 270 name: "one side sets to non-NULL, other drops NULL, plus data change", 271 ancestor: singleRow(1, 2, nil), 272 left: singleRow(1, 3), 273 right: singleRow(1, 2, 3), 274 dataConflict: true, 275 }, 276 { 277 name: "one side sets to non-NULL, other drops non-NULL", 278 ancestor: singleRow(1, 2, 3), 279 left: singleRow(1, 2), 280 right: singleRow(1, 2, 4), 281 dataConflict: true, 282 }, 283 { 284 name: "one side drops column, other deletes row", 285 ancestor: []sql.Row{row(1, 2, 3), row(4, 5, 6)}, 286 left: []sql.Row{row(1, 2), row(4, 5)}, 287 right: []sql.Row{row(1, 2, 3)}, 288 merged: []sql.Row{row(1, 2)}, 289 }, 290 }, 291 }, 292 { 293 name: "left side column drop with additional column after", 294 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)")), 295 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) ")), 296 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)")), 297 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) ")), 298 dataTests: []dataTest{ 299 { 300 name: "no data change", 301 ancestor: singleRow(1, 2, 3), 302 left: singleRow(1, 3), 303 right: singleRow(1, 2, 3), 304 merged: singleRow(1, 3), 305 }, 306 { 307 name: "one side sets to NULL, other drops non-NULL", 308 ancestor: singleRow(1, 2, 3), 309 left: singleRow(1, 3), 310 right: singleRow(1, nil, 3), 311 dataConflict: true, 312 }, 313 { 314 name: "one side sets to NULL, other drops non-NULL, plus data change", 315 ancestor: singleRow(1, 2, 4), 316 left: singleRow(1, 3), 317 right: singleRow(1, nil, 4), 318 dataConflict: true, 319 }, 320 { 321 name: "one side sets to non-NULL, other drops NULL, plus data change", 322 ancestor: singleRow(1, nil, 3), 323 left: singleRow(1, 3), 324 right: singleRow(1, 2, 3), 325 dataConflict: true, 326 }, 327 { 328 name: "one side sets to non-NULL, other drops NULL, plus data change", 329 ancestor: singleRow(1, nil, 3), 330 left: singleRow(1, 4), 331 right: singleRow(1, 2, 3), 332 dataConflict: true, 333 }, 334 { 335 name: "one side sets to non-NULL, other drops non-NULL", 336 ancestor: singleRow(1, 2, 3), 337 left: singleRow(1, 3), 338 right: singleRow(1, 4, 3), 339 dataConflict: true, 340 }, 341 }, 342 }, 343 // both sides change columns 344 { 345 name: "independent column adds", 346 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 347 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) "), row(1, 2)), 348 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) "), row(1, 3)), 349 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3)), 350 skipNewFmt: true, 351 skipOldFmt: true, 352 }, 353 { 354 name: "independent column drops", 355 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3)), 356 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) "), row(1, 2)), 357 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) "), row(1, 3)), 358 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 359 }, 360 { 361 name: "convergent column adds", 362 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) ")), 363 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")), 364 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")), 365 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")), 366 dataTests: []dataTest{ 367 { 368 name: "convergent adds assigning null", 369 ancestor: singleRow(1, 2), 370 left: singleRow(1, 2, nil), 371 right: singleRow(1, 2, nil), 372 merged: singleRow(1, 2, nil), 373 }, 374 { 375 name: "convergent adds with differing nullness", 376 ancestor: singleRow(1, 2), 377 left: singleRow(1, 2, nil), 378 right: singleRow(1, 2, 3), 379 dataConflict: true, 380 }, 381 { 382 name: "convergent adds with differing nullness, plus convergent data change", 383 ancestor: singleRow(1, 2), 384 left: singleRow(1, 3, nil), 385 right: singleRow(1, 3, 4), 386 dataConflict: true, 387 }, 388 }, 389 }, 390 { 391 name: "convergent column add in middle of schema", 392 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) ")), 393 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")), 394 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")), 395 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, a int)")), 396 dataTests: []dataTest{ 397 { 398 name: "convergent adds assigning null", 399 ancestor: singleRow(1, 2), 400 left: singleRow(1, nil, 2), 401 right: singleRow(1, nil, 2), 402 merged: singleRow(1, nil, 2), 403 }, 404 { 405 name: "convergent adds with differing nullness", 406 ancestor: singleRow(1, 2), 407 left: singleRow(1, nil, 2), 408 right: singleRow(1, 3, 2), 409 dataConflict: true, 410 }, 411 { 412 name: "convergent adds with differing nullness, plus convergent data change", 413 ancestor: singleRow(1, 2), 414 left: singleRow(1, nil, 3), 415 right: singleRow(1, 4, 3), 416 dataConflict: true, 417 }, 418 }, 419 }, 420 { 421 name: "convergent column drops", 422 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)")), 423 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY) ")), 424 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY) ")), 425 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) ")), 426 dataTests: []dataTest{ 427 { 428 name: "no data change", 429 ancestor: singleRow(1, 2), 430 left: singleRow(1), 431 right: singleRow(1), 432 merged: singleRow(1), 433 }, 434 { 435 name: "convergent drops on new row", 436 ancestor: nil, 437 left: singleRow(1), 438 right: singleRow(1), 439 merged: singleRow(1), 440 }, 441 }, 442 }, 443 { 444 name: "convergent column adds, independent drops", 445 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3)), 446 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, c int)"), row(1, 3, 4)), 447 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, c int)"), row(1, 2, 4)), 448 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int) "), row(1, 4)), 449 }, 450 { 451 name: "convergent column drops, independent adds", 452 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) "), row(1, 2)), 453 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) "), row(1, 3)), 454 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int) "), row(1, 4)), 455 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int, c int)"), row(1, 3, 4)), 456 skipNewFmt: true, 457 skipOldFmt: true, 458 }, 459 // one side changes columns, the other inserts rows 460 { 461 name: "left side column add, right side insert row", 462 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 463 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, 2)), 464 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1), row(11)), 465 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, 2), row(11, nil)), 466 }, 467 { 468 name: "left side column drop, right side insert row", 469 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, 2)), 470 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 471 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, 2), row(11, 22)), 472 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1), row(11)), 473 }, 474 // both sides change columns and insert rows 475 { 476 name: "independent column adds, both sides insert independent rows", 477 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 478 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) "), row(1, 2), row(12, 22)), 479 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) "), row(1, 3), row(13, 33)), 480 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3), row(12, 22, nil), row(13, nil, 33)), 481 skipNewFmt: true, 482 skipOldFmt: true, 483 }, 484 { 485 name: "independent column drops, both sides insert independent rows", 486 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3)), 487 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) "), row(1, 2), row(12, 22)), 488 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) "), row(1, 3), row(13, 33)), 489 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1), row(12), row(13)), 490 }, 491 { 492 name: "convergent column adds, both sides insert independent rows", 493 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 494 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, nil), row(12, 22)), 495 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, nil), row(13, 33)), 496 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, nil), row(12, 22), row(13, 33)), 497 }, 498 { 499 name: "convergent column drops, both sides insert independent rows", 500 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int)"), row(1, 2)), 501 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1), row(12)), 502 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1), row(13)), 503 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1), row(12), row(13)), 504 }, 505 { 506 name: "independent column adds, both sides insert same row", 507 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 508 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) "), row(1, 2), row(12, 22)), 509 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) "), row(1, 3), row(12, 33)), 510 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3), row(12, 22, 33)), 511 skipNewFmt: true, 512 skipOldFmt: true, 513 }, 514 { 515 name: "independent column drops, both sides insert same row", 516 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int)"), row(1, 2, 3)), 517 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) "), row(1, 2), row(12, 22)), 518 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int) "), row(1, 3), row(12, 33)), 519 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1), row(12)), 520 }, 521 { 522 name: "right side drops and adds column of same type", 523 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, a int)")), 524 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, a int)")), 525 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, b int)")), 526 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, b int)")), 527 dataTests: []dataTest{ 528 { 529 name: "left side modifies dropped column", 530 ancestor: singleRow(1, 1, 2), 531 left: singleRow(1, 1, 3), 532 right: singleRow(1, 2, 2), 533 dataConflict: true, 534 }, 535 }, 536 }, 537 { 538 name: "right side drops and adds column of different type", 539 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, a int)")), 540 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, a int)")), 541 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, b text)")), 542 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, c int, b text)")), 543 }, 544 } 545 546 type constraintViolation struct { 547 violationType merge.CvType 548 key, value sql.Row 549 } 550 551 var collationTests = []schemaMergeTest{ 552 { 553 name: "left side changes collation", 554 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) collate utf8mb4_0900_bin unique)")), 555 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) collate utf8mb4_0900_ai_ci unique)")), 556 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) collate utf8mb4_0900_bin unique)")), 557 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) collate utf8mb4_0900_ai_ci unique)")), 558 dataTests: []dataTest{ 559 { 560 name: "no data change", 561 ancestor: singleRow(1, "hello"), 562 left: singleRow(1, "hello"), 563 right: singleRow(1, "hello"), 564 merged: singleRow(1, "hello"), 565 }, 566 { 567 name: "right side insert", 568 ancestor: []sql.Row{{1, "hello"}}, 569 left: []sql.Row{{1, "hello"}}, 570 right: []sql.Row{{1, "hello"}, {2, "world"}}, 571 merged: []sql.Row{{1, "hello"}, {2, "world"}}, 572 }, 573 { 574 name: "right side delete", 575 ancestor: []sql.Row{{1, "hello"}, {2, "world"}}, 576 left: []sql.Row{{1, "hello"}, {2, "world"}}, 577 right: []sql.Row{{1, "hello"}}, 578 merged: []sql.Row{{1, "hello"}}, 579 }, 580 { 581 name: "right side insert causes unique violation", 582 ancestor: []sql.Row{{1, "hello"}}, 583 left: []sql.Row{{1, "hello"}}, 584 right: []sql.Row{{1, "hello"}, {2, "HELLO"}}, 585 constraintViolations: []constraintViolation{ 586 {merge.CvType_UniqueIndex, sql.Row{int32(1)}, sql.Row{"hello"}}, 587 {merge.CvType_UniqueIndex, sql.Row{int32(2)}, sql.Row{"HELLO"}}, 588 }, 589 }, 590 }, 591 }, 592 { 593 name: "left side changes table collation", 594 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) unique) collate utf8mb4_0900_bin")), 595 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) unique) collate utf8mb4_0900_ai_ci")), 596 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) unique) collate utf8mb4_0900_bin")), 597 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(10) unique) collate utf8mb4_0900_ai_ci")), 598 dataTests: []dataTest{ 599 { 600 name: "no data change", 601 ancestor: singleRow(1, "hello"), 602 left: singleRow(1, "hello"), 603 right: singleRow(1, "hello"), 604 merged: singleRow(1, "hello"), 605 }, 606 { 607 name: "right side insert", 608 ancestor: []sql.Row{{1, "hello"}}, 609 left: []sql.Row{{1, "hello"}}, 610 right: []sql.Row{{1, "hello"}, {2, "world"}}, 611 merged: []sql.Row{{1, "hello"}, {2, "world"}}, 612 }, 613 { 614 name: "right side delete", 615 ancestor: []sql.Row{{1, "hello"}, {2, "world"}}, 616 left: []sql.Row{{1, "hello"}, {2, "world"}}, 617 right: []sql.Row{{1, "hello"}}, 618 merged: []sql.Row{{1, "hello"}}, 619 }, 620 { 621 name: "right side insert causes unique violation", 622 ancestor: []sql.Row{{1, "hello"}}, 623 left: []sql.Row{{1, "hello"}}, 624 right: []sql.Row{{1, "hello"}, {2, "HELLO"}}, 625 constraintViolations: []constraintViolation{ 626 {merge.CvType_UniqueIndex, sql.Row{int32(1)}, sql.Row{"hello"}}, 627 {merge.CvType_UniqueIndex, sql.Row{int32(2)}, sql.Row{"HELLO"}}, 628 }, 629 }, 630 }, 631 }, 632 { 633 name: "no collation changes", 634 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, c varchar(10) collate utf8mb4_0900_ai_ci unique, d decimal(5,3) unique)")), 635 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, c varchar(10) collate utf8mb4_0900_ai_ci unique, d decimal(5,3) unique)")), 636 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, c varchar(10) collate utf8mb4_0900_ai_ci unique, d decimal(5,3) unique)")), 637 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, c varchar(10) collate utf8mb4_0900_ai_ci unique, d decimal(5,3) unique)")), 638 dataTests: []dataTest{ 639 { 640 name: "no data change", 641 ancestor: singleRow(1, 1, 1, "foo", decimal.New(8, 0)), 642 left: singleRow(1, 1, 2, "foo", decimal.New(8, 0)), 643 right: singleRow(1, 2, 1, "foo", decimal.New(8, 0)), 644 merged: singleRow(1, 2, 2, "foo", decimal.New(8, 0)), 645 }, 646 { 647 name: "replace varchar with equal replacement", 648 ancestor: singleRow(1, 1, 1, "foo", decimal.New(100, 0)), 649 left: singleRow(1, 1, 2, "FOO", decimal.New(100, 0)), 650 right: singleRow(1, 2, 1, "foo", decimal.New(100, 0)), 651 merged: singleRow(1, 2, 2, "FOO", decimal.New(100, 0)), 652 }, 653 { 654 name: "conflict removal and replace varchar with equal replacement", 655 ancestor: singleRow(1, 1, 1, "foo", decimal.New(100, 0)), 656 left: singleRow(1, 1, 2, "FOO", decimal.New(100, 0)), 657 right: nil, 658 dataConflict: true, 659 }, 660 { 661 name: "replace decimal with equal replacement", 662 ancestor: singleRow(1, 1, 1, "foo", decimal.New(100, 0)), 663 left: singleRow(1, 1, 2, "foo", decimal.New(1, 2)), 664 right: singleRow(1, 2, 1, "foo", decimal.New(100, 0)), 665 merged: singleRow(1, 2, 2, "foo", decimal.New(1, 2)), 666 }, 667 { 668 name: "conflict removal and replace decimal with equal replacement", 669 ancestor: singleRow(1, 1, 1, "foo", decimal.New(100, 0)), 670 left: singleRow(1, 1, 1, "foo", decimal.New(1, 2)), 671 right: nil, 672 merged: nil, 673 }, 674 }, 675 }, 676 } 677 678 var columnDefaultTests = []schemaMergeTest{ 679 { 680 name: "left side add default", 681 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) ")), 682 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")), 683 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) ")), 684 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")), 685 }, 686 { 687 name: "left side drop default", 688 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")), 689 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) ")), 690 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")), 691 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) ")), 692 }, 693 { 694 name: "convergent add", 695 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) ")), 696 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")), 697 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")), 698 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")), 699 }, 700 { 701 name: "convergent drop", 702 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)")), 703 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) ")), 704 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) ")), 705 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) ")), 706 }, 707 // one side changes columns, the other inserts rows 708 { 709 name: "left side column add, right side insert row", 710 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 711 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)"), row(1, 42)), 712 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1), row(12)), 713 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 42)"), row(1, 42), row(12, 42)), 714 }, 715 // both sides change columns and insert rows 716 { 717 name: "independent column adds, both sides insert independent rows", 718 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 719 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 19) "), row(1, 2), row(12, 19)), 720 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, b int DEFAULT 17) "), row(1, 3), row(13, 17)), 721 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 19, b int DEFAULT 17)"), row(1, 2, 3), row(12, 22, 17), row(13, 19, 33)), 722 skipNewFmt: true, 723 skipOldFmt: true, 724 }, 725 { 726 name: "convergent column adds, both sides insert independent rows", 727 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 728 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 19)"), row(1, 19)), 729 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 19)"), row(1, 19)), 730 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int DEFAULT 19)"), row(1, 19)), 731 }, 732 } 733 734 var nullabilityTests = []schemaMergeTest{ 735 { 736 name: "add not null column to empty table", 737 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) ")), 738 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL)")), 739 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY) ")), 740 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL)")), 741 skipOldFmt: true, 742 skipFlipOnOldFormat: true, 743 }, 744 { 745 name: "add not null constraint to existing column", 746 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) "), row(1, 1)), 747 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL)"), row(1, 1)), 748 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int) "), row(1, 1), row(2, 2)), 749 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL)"), row(1, 1), row(2, 2)), 750 skipOldFmt: true, 751 skipFlipOnOldFormat: true, 752 }, 753 { 754 name: "add not null column to non-empty table", 755 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 756 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL DEFAULT '19')"), row(1, 19)), 757 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1), row(2)), 758 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL DEFAULT '19')"), row(1, 19), row(2, 19)), 759 skipOldFmt: true, 760 skipFlipOnOldFormat: true, 761 }, 762 { 763 name: "table delete plus add not null column to empty table", 764 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) ")), 765 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL DEFAULT '19')")), 766 right: nil, 767 conflict: true, 768 skipOldFmt: true, 769 skipFlipOnOldFormat: true, 770 }, 771 { 772 name: "table delete plus add not null column to non-empty table", 773 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 774 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL DEFAULT '19')"), row(1, 19)), 775 right: nil, 776 conflict: true, 777 skipOldFmt: true, 778 skipFlipOnOldFormat: true, 779 }, 780 } 781 782 var columnReorderingTests = []schemaMergeTest{} 783 784 var typeChangeTests = []schemaMergeTest{ 785 { 786 name: "modify column type on the left side", 787 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int) "), row(1, 2, 3)), 788 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b int)"), row(1, "2", 3)), 789 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int) "), row(1, 2, 3)), 790 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b int)"), row(1, "2", 3)), 791 }, 792 { 793 name: "independently modify column type on the both sides", 794 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int) "), row(1, 2, 3)), 795 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b int) "), row(1, "2", 3)), 796 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b char(20)) "), row(1, 2, "3")), 797 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b char(20))"), row(1, "2", "3")), 798 skipNewFmt: true, 799 skipOldFmt: true, 800 }, 801 { 802 name: "convergently modify column type on the both sides", 803 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int) "), row(1, 2, 3)), 804 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b int)"), row(1, "2", 3)), 805 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b int)"), row(1, "2", 3)), 806 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b int)"), row(1, "2", 3)), 807 }, 808 // column changes one side, data changes other side 809 { 810 name: "modify column type on the left side between compatible string types", 811 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(20), b int, c varchar(20))")), 812 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a text, b int, c text)")), 813 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(20), b int, c varchar(20))")), 814 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a text, b int, c text)")), 815 dataTests: []dataTest{ 816 { 817 name: "schema change, no data change", 818 ancestor: singleRow(1, "test", 1, "test"), 819 left: singleRow(1, "test", 1, "test"), 820 right: singleRow(1, "test", 1, "test"), 821 merged: singleRow(1, "test", 1, "test"), 822 }, 823 { 824 name: "insert and schema change on left, no change on right", 825 ancestor: nil, 826 left: singleRow(1, "test", 1, "test"), 827 right: nil, 828 merged: singleRow(1, "test", 1, "test"), 829 }, 830 { 831 name: "insert on right, schema change on left", 832 ancestor: nil, 833 left: nil, 834 right: singleRow(1, "test", 1, "test"), 835 merged: singleRow(1, "test", 1, "test"), 836 }, 837 { 838 name: "data and schema change on left, no change on right", 839 ancestor: singleRow(1, "test", 1, "test"), 840 left: singleRow(1, "hello world", 1, "hello world"), 841 right: singleRow(1, "test", 1, "test"), 842 merged: singleRow(1, "hello world", 1, "hello world"), 843 }, 844 { 845 name: "data change on right, schema change on left", 846 ancestor: singleRow(1, "test", 1, "test"), 847 left: singleRow(1, "test", 1, "test"), 848 right: singleRow(1, "hello world", 1, "hello world"), 849 merged: singleRow(1, "hello world", 1, "hello world"), 850 }, 851 { 852 name: "data set and schema change on left, no change on right", 853 ancestor: singleRow(1, nil, 1, nil), 854 left: singleRow(1, "hello world", 1, "hello world"), 855 right: singleRow(1, nil, 1, nil), 856 merged: singleRow(1, "hello world", 1, "hello world"), 857 }, 858 { 859 name: "data set on right, schema change on left", 860 ancestor: singleRow(1, nil, 1, nil), 861 left: singleRow(1, nil, 1, nil), 862 right: singleRow(1, "hello world", 1, "hello world"), 863 merged: singleRow(1, "hello world", 1, "hello world"), 864 }, 865 { 866 name: "convergent inserts", 867 ancestor: nil, 868 left: singleRow(1, "test", 1, "test"), 869 right: singleRow(1, "test", 1, "test"), 870 merged: singleRow(1, "test", 1, "test"), 871 }, 872 { 873 name: "conflicting inserts", 874 ancestor: nil, 875 left: singleRow(1, "test", 1, "test"), 876 right: singleRow(1, "hello world", 1, "hello world"), 877 dataConflict: true, 878 }, 879 { 880 name: "delete and schema change on left", 881 ancestor: singleRow(1, "test", 1, "test"), 882 left: nil, 883 right: singleRow(1, "test", 1, "test"), 884 merged: nil, 885 }, 886 { 887 name: "schema change on left, delete on right", 888 ancestor: singleRow(1, "test", 1, "test"), 889 left: singleRow(1, "test", 1, "test"), 890 right: nil, 891 merged: nil, 892 }, 893 { 894 name: "schema and value change on left, delete on right", 895 ancestor: singleRow(1, "test", 1, "test"), 896 left: singleRow(1, "hello", 1, "hello"), 897 right: nil, 898 dataConflict: true, 899 }, 900 }, 901 }, 902 { 903 name: "modify column type on the left side between compatible string types with unique secondary index", 904 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(20) unique, b int, c varchar(20) unique)")), 905 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a text, b int, c text)")), 906 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a varchar(20) unique, b int, c varchar(20) unique)")), 907 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a text, b int, c text)")), 908 dataTests: []dataTest{ 909 { 910 name: "schema change, no data change", 911 ancestor: singleRow(1, "test", 1, "test"), 912 left: singleRow(1, "test", 1, "test"), 913 right: singleRow(1, "test", 1, "test"), 914 merged: singleRow(1, "test", 1, "test"), 915 }, 916 { 917 name: "insert and schema change on left, no change on right", 918 ancestor: nil, 919 left: singleRow(1, "test", 1, "test"), 920 right: nil, 921 merged: singleRow(1, "test", 1, "test"), 922 }, 923 { 924 name: "insert on right, schema change on left", 925 ancestor: nil, 926 left: nil, 927 right: singleRow(1, "test", 1, "test"), 928 merged: singleRow(1, "test", 1, "test"), 929 }, 930 { 931 name: "data and schema change on left, no change on right", 932 ancestor: singleRow(1, "test", 1, "test"), 933 left: singleRow(1, "hello world", 1, "hello world"), 934 right: singleRow(1, "test", 1, "test"), 935 merged: singleRow(1, "hello world", 1, "hello world"), 936 }, 937 { 938 name: "data change on right, schema change on left", 939 ancestor: singleRow(1, "test", 1, "test"), 940 left: singleRow(1, "test", 1, "test"), 941 right: singleRow(1, "hello world", 1, "hello world"), 942 merged: singleRow(1, "hello world", 1, "hello world"), 943 }, 944 { 945 name: "data set and schema change on left, no change on right", 946 ancestor: singleRow(1, nil, 1, nil), 947 left: singleRow(1, "hello world", 1, "hello world"), 948 right: singleRow(1, nil, 1, nil), 949 merged: singleRow(1, "hello world", 1, "hello world"), 950 }, 951 { 952 name: "data set on right, schema change on left", 953 ancestor: singleRow(1, nil, 1, nil), 954 left: singleRow(1, nil, 1, nil), 955 right: singleRow(1, "hello world", 1, "hello world"), 956 merged: singleRow(1, "hello world", 1, "hello world"), 957 }, 958 { 959 name: "convergent inserts", 960 ancestor: nil, 961 left: singleRow(1, "test", 1, "test"), 962 right: singleRow(1, "test", 1, "test"), 963 merged: singleRow(1, "test", 1, "test"), 964 }, 965 { 966 name: "conflicting inserts", 967 ancestor: nil, 968 left: singleRow(1, "test", 1, "test"), 969 right: singleRow(1, "hello world", 1, "hello world"), 970 dataConflict: true, 971 }, 972 { 973 name: "delete and schema change on left", 974 ancestor: singleRow(1, "test", 1, "test"), 975 left: nil, 976 right: singleRow(1, "test", 1, "test"), 977 merged: nil, 978 }, 979 { 980 name: "schema change on left, delete on right", 981 ancestor: singleRow(1, "test", 1, "test"), 982 left: singleRow(1, "test", 1, "test"), 983 right: nil, 984 merged: nil, 985 }, 986 { 987 name: "schema and value change on left, delete on right", 988 ancestor: singleRow(1, "test", 1, "test"), 989 left: singleRow(1, "hello", 1, "hello"), 990 right: nil, 991 dataConflict: true, 992 }, 993 }, 994 }, 995 } 996 997 var keyChangeTests = []schemaMergeTest{ 998 { 999 name: "add a trailing primary key column on left side", 1000 ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1001 left: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))), 1002 right: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1003 merged: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))), 1004 }, 1005 { 1006 name: "add a leading primary key column on left side", 1007 ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1008 left: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b, a))"), row(1, "2", float32(3.0))), 1009 right: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1010 merged: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b, a))"), row(1, "2", float32(3.0))), 1011 }, 1012 { 1013 name: "remove a trailing primary key column on left side", 1014 ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))), 1015 left: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1016 right: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))), 1017 merged: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1018 skipFlipOnNewFormat: true, 1019 }, 1020 { 1021 name: "remove a trailing primary key column on both sides", 1022 ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))), 1023 left: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1024 right: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1025 merged: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1026 }, 1027 { 1028 name: "remove a leading primary key column on left side", 1029 ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b, a))"), row(1, "2", float32(3.0))), 1030 left: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1031 right: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b, a))"), row(1, "2", float32(3.0))), 1032 merged: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1033 skipFlipOnNewFormat: true, 1034 }, 1035 { 1036 name: "remove a leading primary key column on both sides", 1037 ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b, a))"), row(1, "2", float32(3.0))), 1038 left: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1039 right: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1040 merged: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1041 }, 1042 { 1043 skipFlipOnNewFormat: true, 1044 skipFlipOnOldFormat: true, 1045 name: "convert left side to a keyless table", 1046 ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))"), row(1, "2", float32(3.0))), 1047 left: tbl(sch("CREATE TABLE t (a int, b char(20), c float) "), row(1, "2", float32(3.0))), 1048 right: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))"), row(1, "2", float32(3.0))), 1049 merged: *tbl(sch("CREATE TABLE t (a int, b char(20), c float) "), row(1, "2", float32(3.0))), 1050 }, 1051 { 1052 name: "convert both sides to keyless tables", 1053 ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a))"), row(1, "2", float32(3.0))), 1054 left: tbl(sch("CREATE TABLE t (a int, b char(20), c float) "), row(1, "2", float32(3.0))), 1055 right: tbl(sch("CREATE TABLE t (a int, b char(20), c float) "), row(1, "2", float32(3.0))), 1056 merged: *tbl(sch("CREATE TABLE t (a int, b char(20), c float) "), row(1, "2", float32(3.0))), 1057 skipNewFmt: true, 1058 skipOldFmt: true, 1059 }, 1060 } 1061 1062 var secondaryIndexTests = []schemaMergeTest{ 1063 { 1064 name: "independent index adds", 1065 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float) "), row(1, "2", float32(3.0))), 1066 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (a)) "), row(1, "2", float32(3.0))), 1067 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (b)) "), row(1, "2", float32(3.0))), 1068 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (a), INDEX(b))"), row(1, "2", float32(3.0))), 1069 }, 1070 { 1071 name: "independent composite index adds", 1072 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float) "), row(1, "2", float32(3.0))), 1073 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (a, b)) "), row(1, "2", float32(3.0))), 1074 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (b, a)) "), row(1, "2", float32(3.0))), 1075 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (a, b), INDEX (b, a))"), row(1, "2", float32(3.0))), 1076 }, 1077 { 1078 name: "independent index drops", 1079 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (a), INDEX (b))"), row(1, "2", float32(3.0))), 1080 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (a)) "), row(1, "2", float32(3.0))), 1081 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX (b)) "), row(1, "2", float32(3.0))), 1082 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float) "), row(1, "2", float32(3.0))), 1083 skipOldFmt: true, 1084 skipFlipOnOldFormat: true, 1085 }, 1086 } 1087 1088 var simpleConflictTests = []schemaMergeTest{ 1089 { 1090 name: "conflicting column adds", 1091 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 1092 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NULL) "), row(1, 2)), 1093 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NOT NULL)"), row(1, 2)), 1094 conflict: true, 1095 }, 1096 { 1097 name: "column add and table drop", 1098 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY) "), row(1)), 1099 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int NULL) "), row(1, 2)), 1100 right: nil, 1101 conflict: true, 1102 }, 1103 { 1104 // TODO: This test case does NOT generate a conflict; the merge gets short circuited, because the table's 1105 // right/left/anc hashes are all the same. This is an issue with the test framework, not with Dolt. 1106 // The code we use in these tests to create a schema (sqlutil.ParseCreateTableStatement) silently 1107 // drops index and check constraint definitions. 1108 skipNewFmt: true, 1109 skipOldFmt: true, 1110 name: "conflicting index adds: same name and columns, different constraints", 1111 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float) ")), 1112 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX idx (a)) ")), 1113 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, UNIQUE INDEX idx (a))")), 1114 conflict: true, 1115 }, 1116 { 1117 // TODO: This test case does NOT generate a conflict; the merge gets short circuited, because the table's 1118 // right/left/anc hashes are all the same. This is an issue with the test framework, not with Dolt. 1119 // The code we use in these tests to create a schema (sqlutil.ParseCreateTableStatement) silently 1120 // drops index and check constraint definitions. 1121 skipNewFmt: true, 1122 skipOldFmt: true, 1123 // TODO: multiple indexes can exist for the same column set, so this shouldn't actually be a conflict; 1124 // Dolt does report this as a schema conflict today, but we could merge the two indexes together. 1125 name: "conflicting index adds: same column different names", 1126 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float) ")), 1127 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX a_idx (a))")), 1128 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX key_a (a))")), 1129 conflict: true, 1130 }, 1131 { 1132 // TODO: This test case does NOT generate a conflict; the merge gets short circuited, because the table's 1133 // right/left/anc hashes are all the same. This is an issue with the test framework, not with Dolt. 1134 // The code we use in these tests to create a schema (sqlutil.ParseCreateTableStatement) silently 1135 // drops index and check constraint definitions. 1136 skipNewFmt: true, 1137 skipOldFmt: true, 1138 name: "conflicting index adds: same name different definitions", 1139 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float) ")), 1140 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX idx (a))")), 1141 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a char(20), b float, INDEX idx (b))")), 1142 conflict: true, 1143 }, 1144 { 1145 name: "add primary key columns at different key positions on left and right sides", 1146 ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1147 left: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))), 1148 right: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b, a))"), row(1, "2", float32(3.0))), 1149 conflict: true, 1150 }, 1151 { 1152 name: "remove different primary key columns on left and right sides", 1153 ancestor: *tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a, b))"), row(1, "2", float32(3.0))), 1154 left: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (a)) "), row(1, "2", float32(3.0))), 1155 right: tbl(sch("CREATE TABLE t (a int, b char(20), c float, PRIMARY KEY (b)) "), row(1, "2", float32(3.0))), 1156 conflict: true, 1157 }, 1158 } 1159 1160 var jsonMergeTests = []schemaMergeTest{ 1161 { 1162 name: "json merge", 1163 ancestor: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, j json)")), 1164 left: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, j json)")), 1165 right: tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, j json)")), 1166 merged: *tbl(sch("CREATE TABLE t (id int PRIMARY KEY, a int, b int, j json)")), 1167 dataTests: []dataTest{ 1168 { 1169 name: "convergent insertion", 1170 ancestor: singleRow(1, 1, 1, `{}`), 1171 left: singleRow(1, 2, 1, `{ "key1": "value1" }`), 1172 right: singleRow(1, 1, 2, `{ "key1": "value1" }`), 1173 merged: singleRow(1, 2, 2, `{ "key1": "value1" }`), 1174 }, 1175 { 1176 name: "convergent insertion with escaped quotes in keys", 1177 ancestor: singleRow(1, 1, 1, `{}`), 1178 left: singleRow(1, 2, 1, `{ "\"key1\"": "\"value1\"" }`), 1179 right: singleRow(1, 1, 2, `{ "\"key1\"": "\"value1\"" }`), 1180 merged: singleRow(1, 2, 2, `{ "\"key1\"": "\"value1\"" }`), 1181 }, 1182 { 1183 name: `parallel insertion`, 1184 ancestor: singleRow(1, 1, 1, `{}`), 1185 left: singleRow(1, 2, 1, `{ "key1": "value1" }`), 1186 right: singleRow(1, 1, 2, `{ "key2": "value2" }`), 1187 merged: singleRow(1, 2, 2, `{ "key1": "value1", "key2": "value2" }`), 1188 }, 1189 { 1190 name: `convergent modification`, 1191 ancestor: singleRow(1, 1, 1, `{ "key1": "value1" }`), 1192 left: singleRow(1, 2, 1, `{ "key1": "value2" }`), 1193 right: singleRow(1, 1, 2, `{ "key1": "value2" }`), 1194 merged: singleRow(1, 2, 2, `{ "key1": "value2" }`), 1195 }, 1196 { 1197 name: `parallel modification`, 1198 ancestor: singleRow(1, 1, 1, `{ "key1": "value1", "key2": "value2" }`), 1199 left: singleRow(1, 2, 1, `{ "key1": "value3", "key2": "value2" }`), 1200 right: singleRow(1, 1, 2, `{ "key1": "value1", "key2": "value4" }`), 1201 merged: singleRow(1, 2, 2, `{ "key1": "value3", "key2": "value4" }`), 1202 }, 1203 { 1204 name: `parallel deletion`, 1205 ancestor: singleRow(1, 1, 1, `{ "key1": "value1" }`), 1206 left: singleRow(1, 2, 1, `{}`), 1207 right: singleRow(1, 1, 2, `{}`), 1208 merged: singleRow(1, 2, 2, `{}`), 1209 }, 1210 { 1211 name: `convergent deletion`, 1212 ancestor: singleRow(1, 1, 1, `{ "key1": "value1", "key2": "value2" }`), 1213 left: singleRow(1, 2, 1, `{ "key2": "value2" }`), 1214 right: singleRow(1, 1, 2, `{ "key1": "value1" }`), 1215 merged: singleRow(1, 2, 2, `{}`), 1216 }, 1217 { 1218 name: `divergent insertion`, 1219 ancestor: singleRow(1, 1, 1, `{}`), 1220 left: singleRow(1, 2, 1, `{ "key1": "value1" }`), 1221 right: singleRow(1, 1, 2, `{ "key1": "value2" }`), 1222 dataConflict: true, 1223 }, 1224 { 1225 name: `divergent modification`, 1226 ancestor: singleRow(1, 1, 1, `{ "key1": "value1"}`), 1227 left: singleRow(1, 2, 1, `{ "key1": "value2" }`), 1228 right: singleRow(1, 1, 2, `{ "key1": "value3" }`), 1229 dataConflict: true, 1230 }, 1231 { 1232 name: `divergent modification and deletion`, 1233 ancestor: singleRow(1, 1, 1, `{ "key1": "value1"}`), 1234 left: singleRow(1, 2, 1, `{ "key1": "value2" }`), 1235 right: singleRow(1, 1, 2, `{}`), 1236 dataConflict: true, 1237 }, 1238 { 1239 name: `nested insertion`, 1240 ancestor: singleRow(1, 1, 1, `{ "key1": {} }`), 1241 left: singleRow(1, 2, 1, `{ "key1": { "key1a": "value1a" } }`), 1242 right: singleRow(1, 1, 2, `{ "key1": { "key1b": "value1b" } }`), 1243 merged: singleRow(1, 2, 2, `{ "key1": { "key1a": "value1a", "key1b": "value1b" } }`), 1244 }, 1245 { 1246 name: `nested insertion with escaped quotes in keys`, 1247 ancestor: singleRow(1, 1, 1, `{ "\"key1\"": {} }`), 1248 left: singleRow(1, 2, 1, `{ "\"key1\"": { "\"key1a\"": "value1a" } }`), 1249 right: singleRow(1, 1, 2, `{ "\"key1\"": { "\"key1b\"": "value1b" } }`), 1250 merged: singleRow(1, 2, 2, `{ "\"key1\"": { "\"key1a\"": "value1a", "\"key1b\"": "value1b" } }`), 1251 }, 1252 { 1253 name: `nested modification`, 1254 ancestor: singleRow(1, 1, 1, `{ "key1": { "key1a": "value1a", "key1b": "value1b" } }`), 1255 left: singleRow(1, 2, 1, `{ "key1": { "key1a": "value2a", "key1b": "value1b" } }`), 1256 right: singleRow(1, 1, 2, `{ "key1": { "key1a": "value1a", "key1b": "value2b" } }`), 1257 merged: singleRow(1, 2, 2, `{ "key1": { "key1a": "value2a", "key1b": "value2b" } }`), 1258 }, 1259 { 1260 name: `nested modification with escaped quotes in keys`, 1261 ancestor: singleRow(1, 1, 1, `{ "\"key1\"": { "\"key1a\"": "value1a", "\"key1b\"": "value1b" } }`), 1262 left: singleRow(1, 2, 1, `{ "\"key1\"": { "\"key1a\"": "value2a", "\"key1b\"": "value1b" } }`), 1263 right: singleRow(1, 1, 2, `{ "\"key1\"": { "\"key1a\"": "value1a", "\"key1b\"": "value2b" } }`), 1264 merged: singleRow(1, 2, 2, `{ "\"key1\"": { "\"key1a\"": "value2a", "\"key1b\"": "value2b" } }`), 1265 }, 1266 { 1267 name: `nested deletion`, 1268 ancestor: singleRow(1, 1, 1, `{ "key1": { "key1a": "value1a", "key1b": "value1b" } }`), 1269 left: singleRow(1, 2, 1, `{ "key1": { "key1a": "value1a" } }`), 1270 right: singleRow(1, 1, 2, `{ "key1": { "key1b": "value1b" } }`), 1271 merged: singleRow(1, 2, 2, `{ "key1": { } }`), 1272 }, 1273 { 1274 name: `nested deletion with escaped quotes in keys`, 1275 ancestor: singleRow(1, 1, 1, `{ "\"key1\"": { "\"key1a\"": "value1a", "\"key1b\"": "value1b" } }`), 1276 left: singleRow(1, 2, 1, `{ "\"key1\"": { "\"key1a\"": "value1a" } }`), 1277 right: singleRow(1, 1, 2, `{ "\"key1\"": { "\"key1b\"": "value1b" } }`), 1278 merged: singleRow(1, 2, 2, `{ "\"key1\"": { } }`), 1279 }, 1280 { 1281 name: "complicated nested merge", 1282 ancestor: singleRow(1, 1, 1, `{ "removed": 1, "modified": 2, "nested": { "removed": 3, "modified": 4 } }`), 1283 left: singleRow(1, 2, 1, `{ "added": 7, "modified": 2, "nested": { "removed": 3, "modified": 5 } }`), 1284 right: singleRow(1, 1, 2, `{ "removed": 1, "modified": 6, "nested": { "added": 8, "modified": 4 } }`), 1285 merged: singleRow(1, 2, 2, `{ "added": 7, "modified": 6, "nested": { "added": 8, "modified": 5 } }`), 1286 }, 1287 { 1288 name: "object with double quotes in keys", 1289 ancestor: singleRow(1, 1, 1, `{ "\"removed\"": 1, "\"modified\"": 2, "\"nested\"": { "\"removed\"": 3, "\"modified\"": 4 } }`), 1290 left: singleRow(1, 2, 1, `{ "\"added\"": 7, "\"modified\"": 2, "\"nested\"": { "\"removed\"": 3, "\"modified\"": 5 } }`), 1291 right: singleRow(1, 1, 2, `{ "\"removed\"": 1, "\"modified\"": 6, "\"nested\"": { "\"added\"": 8, "\"modified\"": 4 } }`), 1292 merged: singleRow(1, 2, 2, `{ "\"added\"": 7, "\"modified\"": 6, "\"nested\"": { "\"added\"": 8, "\"modified\"": 5 } }`), 1293 }, 1294 { 1295 name: "changing types", 1296 ancestor: singleRow(1, 1, 1, `{ "key1": {}, "key2": 2 }`), 1297 left: singleRow(1, 2, 1, `{ "key1": [], "key2": 2 }`), 1298 right: singleRow(1, 1, 2, `{ "key1": {}, "key2": true }`), 1299 merged: singleRow(1, 2, 2, `{ "key1": [], "key2": true }`), 1300 }, 1301 { 1302 name: "changing types conflict", 1303 ancestor: singleRow(1, 1, 1, `{ "key1": {} }`), 1304 left: singleRow(1, 2, 1, `{ "key1": [] }`), 1305 right: singleRow(1, 1, 2, `{ "key1": 2 }`), 1306 dataConflict: true, 1307 }, 1308 { 1309 name: "object insert and modify conflict", 1310 ancestor: singleRow(1, 1, 1, `{ "key1": {} }`), 1311 left: singleRow(1, 2, 1, `{ "key1": { "key2": 2 } }`), 1312 right: singleRow(1, 1, 2, `{ "key1": 2 }`), 1313 dataConflict: true, 1314 }, 1315 { 1316 name: "object insert and delete conflict", 1317 ancestor: singleRow(1, 1, 1, `{ "key1": {} }`), 1318 left: singleRow(1, 2, 1, `{ "key1": { "key2": 2 } }`), 1319 right: singleRow(1, 1, 2, `{ }`), 1320 dataConflict: true, 1321 }, 1322 { 1323 name: "changing arrays conflict", 1324 ancestor: singleRow(1, 1, 1, `{ "key1": [1] }`), 1325 left: singleRow(1, 2, 1, `{ "key1": [1, 1] }`), 1326 right: singleRow(1, 1, 2, `{ "key1": [] }`), 1327 dataConflict: true, 1328 }, 1329 { 1330 // Which array element should go first? 1331 // We avoid making assumptions and flag this as a conflict. 1332 name: "object inside array conflict", 1333 ancestor: singleRow(1, 1, 1, `{ "key1": [ { } ] }`), 1334 left: singleRow(1, 2, 1, `{ "key1": [ { "key2": "value2" } ] }`), 1335 right: singleRow(1, 1, 2, `{ "key1": [ { "key3": "value3" } ] }`), 1336 dataConflict: true, 1337 }, 1338 { 1339 // Did the left branch overwrite the first value in the array? 1340 // Or did it remove the last value and insert at the beginning? 1341 // Did the right branch overwrite the second value in the array? 1342 // Or did it remove the first value and insert at the end? 1343 // Diffs on arrays are ambiguous. We avoid making assumptions and flag this as a conflict. 1344 name: "parallel array modification", 1345 ancestor: singleRow(1, 1, 1, `{ "key1": [ 1, 1 ] }`), 1346 left: singleRow(1, 2, 1, `{ "key1": [ 2, 1 ] }`), 1347 right: singleRow(1, 1, 2, `{ "key1": [ 1, 2 ] }`), 1348 dataConflict: true, 1349 }, 1350 }, 1351 }, 1352 } 1353 1354 func testSchemaMerge(t *testing.T, tests []schemaMergeTest) { 1355 t.Run("merge left to right", func(t *testing.T) { 1356 testSchemaMergeHelper(t, tests, false) 1357 }) 1358 t.Run("merge right to left", func(t *testing.T) { 1359 testSchemaMergeHelper(t, tests, true) 1360 }) 1361 } 1362 1363 func testSchemaMergeHelper(t *testing.T, tests []schemaMergeTest, flipSides bool) { 1364 for _, test := range tests { 1365 if flipSides { 1366 tmp := test.left 1367 test.left = test.right 1368 test.right = tmp 1369 for i, _ := range test.dataTests { 1370 tmp := test.dataTests[i].left 1371 test.dataTests[i].left = test.dataTests[i].right 1372 test.dataTests[i].right = tmp 1373 } 1374 } 1375 1376 t.Run(test.name, func(t *testing.T) { 1377 runTest := func(t *testing.T, test schemaMergeTest, expectDataConflict bool, expConstraintViolations []constraintViolation) { 1378 a, l, r, m := setupSchemaMergeTest(t, test) 1379 1380 ctx := context.Background() 1381 var mo merge.MergeOpts 1382 var eo editor.Options 1383 eo = eo.WithDeaf(editor.NewInMemDeaf(a.VRW())) 1384 // attempt merge before skipping to assert no panics 1385 result, err := merge.MergeRoots(sql.NewContext(ctx), l, r, a, rootish{r}, rootish{a}, eo, mo) 1386 maybeSkip(t, a.VRW().Format(), test, flipSides) 1387 1388 if test.conflict { 1389 // TODO: Test the conflict error message more deeply 1390 require.Error(t, err) 1391 } else { 1392 require.NoError(t, err) 1393 exp, err := doltdb.MapTableHashes(ctx, m) 1394 assert.NoError(t, err) 1395 act, err := doltdb.MapTableHashes(ctx, result.Root) 1396 assert.NoError(t, err) 1397 1398 assert.Equal(t, len(exp), len(act)) 1399 1400 if expectDataConflict { 1401 foundDataConflict := false 1402 for name, _ := range exp { 1403 _, ok := act[name] 1404 assert.True(t, ok) 1405 actTbl, _, err := result.Root.GetTable(ctx, doltdb.TableName{Name: name}) 1406 require.NoError(t, err) 1407 hasConflict, err := actTbl.HasConflicts(ctx) 1408 require.NoError(t, err) 1409 foundDataConflict = foundDataConflict || hasConflict 1410 } 1411 if !assert.True(t, foundDataConflict, "Expected data conflict, but didn't find one.") { 1412 for name, _ := range exp { 1413 table, _, err := result.Root.GetTable(ctx, doltdb.TableName{Name: name}) 1414 require.NoError(t, err) 1415 t.Logf("table %s:", name) 1416 t.Log(table.DebugString(ctx, m.NodeStore())) 1417 } 1418 1419 } 1420 } else { 1421 for name, addr := range exp { 1422 a, ok := act[name] 1423 assert.True(t, ok) 1424 1425 actTbl, _, err := result.Root.GetTable(ctx, doltdb.TableName{Name: name}) 1426 require.NoError(t, err) 1427 hasConflict, err := actTbl.HasConflicts(ctx) 1428 require.NoError(t, err) 1429 require.False(t, hasConflict, "Unexpected data conflict") 1430 1431 numConstraintViolations, err := actTbl.NumConstraintViolations(ctx) 1432 require.NoError(t, err) 1433 require.EqualValues(t, numConstraintViolations, len(expConstraintViolations)) 1434 1435 sch, err := actTbl.GetSchema(ctx) 1436 require.NoError(t, err) 1437 kd, vd := sch.GetMapDescriptors() 1438 1439 if len(expConstraintViolations) > 0 { 1440 artifacts, err := actTbl.GetArtifacts(ctx) 1441 require.NoError(t, err) 1442 artifactMap := durable.ProllyMapFromArtifactIndex(artifacts) 1443 artifactIter, err := artifactMap.IterAllCVs(ctx) 1444 require.NoError(t, err) 1445 1446 // value tuples encoded in ConstraintViolationMeta may 1447 // violate the not null constraints assumed by fixed access 1448 kd = kd.WithoutFixedAccess() 1449 vd = vd.WithoutFixedAccess() 1450 for _, expectedViolation := range expConstraintViolations { 1451 violationType, key, value, err := merge.NextConstraintViolation(ctx, artifactIter, kd, vd, artifactMap.NodeStore()) 1452 require.NoError(t, err) 1453 require.EqualValues(t, expectedViolation.violationType, violationType) 1454 require.EqualValues(t, expectedViolation.key, key) 1455 require.EqualValues(t, expectedViolation.value, value) 1456 } 1457 } else { 1458 if addr != a { 1459 expTbl, _, err := m.GetTable(ctx, doltdb.TableName{Name: name}) 1460 require.NoError(t, err) 1461 expSchema, err := expTbl.GetSchema(ctx) 1462 require.NoError(t, err) 1463 expRowDataHash, err := expTbl.GetRowDataHash(ctx) 1464 require.NoError(t, err) 1465 actRowDataHash, err := actTbl.GetRowDataHash(ctx) 1466 require.NoError(t, err) 1467 if !expSchema.GetKeyDescriptor().Equals(kd) { 1468 t.Fatal("Primary key descriptors unequal") 1469 } 1470 if !expSchema.GetValueDescriptor().Equals(vd) { 1471 t.Fatal("Value descriptors unequal") 1472 } 1473 if expRowDataHash != actRowDataHash { 1474 t.Error("Rows unequal") 1475 t.Logf("expected rows: %s", expTbl.DebugString(ctx, m.NodeStore())) 1476 t.Logf("actual rows: %s", actTbl.DebugString(ctx, m.NodeStore())) 1477 } 1478 expIndexSet, err := expTbl.GetIndexSet(ctx) 1479 require.NoError(t, err) 1480 actIndexSet, err := actTbl.GetIndexSet(ctx) 1481 require.NoError(t, err) 1482 expSchema.Indexes().Iter(func(index schema.Index) (stop bool, err error) { 1483 expIndex, err := expIndexSet.GetIndex(ctx, expSchema, index.Name()) 1484 require.NoError(t, err) 1485 actIndex, err := actIndexSet.GetIndex(ctx, expSchema, index.Name()) 1486 require.NoError(t, err) 1487 expIndexHash, err := expIndex.HashOf() 1488 require.NoError(t, err) 1489 actIndexHash, err := actIndex.HashOf() 1490 require.NoError(t, err) 1491 if expIndexHash != actIndexHash { 1492 t.Errorf("Index %s unequal", index.Name()) 1493 t.Logf("expected rows: %s", expIndex.DebugString(ctx, m.NodeStore(), expSchema)) 1494 t.Logf("actual rows: %s", actIndex.DebugString(ctx, m.NodeStore(), expSchema)) 1495 } 1496 return false, nil 1497 }) 1498 1499 } 1500 } 1501 } 1502 } 1503 } 1504 } 1505 t.Run("test schema merge", func(t *testing.T) { 1506 runTest(t, test, false, nil) 1507 }) 1508 for _, data := range test.dataTests { 1509 // Copy the test so that the values from one data test don't affect subsequent data tests. 1510 dataTest := test 1511 newLeft := *test.left 1512 newRight := *test.right 1513 dataTest.left = &newLeft 1514 dataTest.right = &newRight 1515 dataTest.ancestor.rows = data.ancestor 1516 dataTest.left.rows = data.left 1517 dataTest.right.rows = data.right 1518 dataTest.merged.rows = data.merged 1519 dataTest.skipNewFmt = dataTest.skipNewFmt || data.skip 1520 dataTest.skipFlipOnNewFormat = dataTest.skipFlipOnNewFormat || data.skipFlip 1521 t.Run(data.name, func(t *testing.T) { 1522 if data.skip { 1523 t.Skip() 1524 } 1525 runTest(t, dataTest, data.dataConflict, data.constraintViolations) 1526 }) 1527 } 1528 }) 1529 } 1530 } 1531 1532 func setupSchemaMergeTest(t *testing.T, test schemaMergeTest) (anc, left, right, merged doltdb.RootValue) { 1533 denv := dtestutils.CreateTestEnv() 1534 var eo editor.Options 1535 eo = eo.WithDeaf(editor.NewInMemDeaf(denv.DoltDB.ValueReadWriter())) 1536 anc = makeRootWithTable(t, denv.DoltDB, eo, test.ancestor) 1537 assert.NotNil(t, anc) 1538 if test.left != nil { 1539 left = makeRootWithTable(t, denv.DoltDB, eo, *test.left) 1540 assert.NotNil(t, left) 1541 } else { 1542 left = makeEmptyRoot(t, denv.DoltDB, eo) 1543 } 1544 if test.right != nil { 1545 right = makeRootWithTable(t, denv.DoltDB, eo, *test.right) 1546 assert.NotNil(t, right) 1547 } else { 1548 right = makeEmptyRoot(t, denv.DoltDB, eo) 1549 } 1550 if !test.conflict { 1551 merged = makeRootWithTable(t, denv.DoltDB, eo, test.merged) 1552 assert.NotNil(t, merged) 1553 } 1554 return 1555 } 1556 1557 func maybeSkip(t *testing.T, nbf *types.NomsBinFormat, test schemaMergeTest, flipSides bool) { 1558 if types.IsFormat_DOLT(nbf) { 1559 if test.skipNewFmt || flipSides && test.skipFlipOnNewFormat { 1560 t.Skip() 1561 } 1562 } else { 1563 if test.skipOldFmt || flipSides && test.skipFlipOnOldFormat { 1564 t.Skip() 1565 } 1566 } 1567 } 1568 1569 func tbl(ns namedSchema, rows ...sql.Row) *table { 1570 return &table{ns: ns, rows: rows} 1571 } 1572 1573 func sch(definition string) namedSchema { 1574 denv := dtestutils.CreateTestEnv() 1575 vrw := denv.DoltDB.ValueReadWriter() 1576 ns := denv.DoltDB.NodeStore() 1577 ctx := context.Background() 1578 root, _ := doltdb.EmptyRootValue(ctx, vrw, ns) 1579 eng, dbName, _ := engine.NewSqlEngineForEnv(ctx, denv) 1580 sqlCtx, _ := eng.NewDefaultContext(ctx) 1581 sqlCtx.SetCurrentDatabase(dbName) 1582 // TODO: ParseCreateTableStatement silently drops any indexes or check constraints in the definition 1583 name, s, err := sqlutil.ParseCreateTableStatement(sqlCtx, root, eng.GetUnderlyingEngine(), definition) 1584 if err != nil { 1585 panic(err) 1586 } 1587 return namedSchema{name: name, sch: s, create: definition} 1588 } 1589 1590 func row(values ...any) sql.Row { 1591 return sql.NewRow(values...) 1592 } 1593 1594 func singleRow(values ...any) []sql.Row { 1595 return []sql.Row{row(values...)} 1596 } 1597 func makeEmptyRoot(t *testing.T, ddb *doltdb.DoltDB, eo editor.Options) doltdb.RootValue { 1598 ctx := context.Background() 1599 wsr, err := ref.WorkingSetRefForHead(ref.NewBranchRef("main")) 1600 require.NoError(t, err) 1601 ws, err := ddb.ResolveWorkingSet(ctx, wsr) 1602 require.NoError(t, err) 1603 1604 gst, err := dsess.NewAutoIncrementTracker(ctx, "dolt", ws) 1605 require.NoError(t, err) 1606 sess := writer.NewWriteSession(ddb.Format(), ws, gst, eo) 1607 1608 ws, err = sess.Flush(sql.NewContext(ctx)) 1609 require.NoError(t, err) 1610 return ws.WorkingRoot() 1611 } 1612 1613 func makeRootWithTable(t *testing.T, ddb *doltdb.DoltDB, eo editor.Options, tbl table) doltdb.RootValue { 1614 ctx := context.Background() 1615 wsr, err := ref.WorkingSetRefForHead(ref.NewBranchRef("main")) 1616 require.NoError(t, err) 1617 ws, err := ddb.ResolveWorkingSet(ctx, wsr) 1618 require.NoError(t, err) 1619 dt, err := doltdb.NewEmptyTable(ctx, ddb.ValueReadWriter(), ddb.NodeStore(), tbl.ns.sch) 1620 require.NoError(t, err) 1621 root, err := ws.WorkingRoot().PutTable(ctx, doltdb.TableName{Name: tbl.ns.name}, dt) 1622 require.NoError(t, err) 1623 ws = ws.WithWorkingRoot(root) 1624 1625 gst, err := dsess.NewAutoIncrementTracker(ctx, "dolt", ws) 1626 require.NoError(t, err) 1627 noop := func(ctx *sql.Context, dbName string, root doltdb.RootValue) (err error) { return } 1628 sess := writer.NewWriteSession(ddb.Format(), ws, gst, eo) 1629 wr, err := sess.GetTableWriter(sql.NewContext(ctx), doltdb.TableName{Name: tbl.ns.name}, "test", noop) 1630 require.NoError(t, err) 1631 1632 sctx := sql.NewEmptyContext() 1633 for _, r := range tbl.rows { 1634 err = wr.Insert(sctx, r) 1635 assert.NoError(t, err) 1636 } 1637 ws, err = sess.Flush(sql.NewContext(ctx)) 1638 require.NoError(t, err) 1639 return ws.WorkingRoot() 1640 } 1641 1642 type rootish struct { 1643 rv doltdb.RootValue 1644 } 1645 1646 func (r rootish) ResolveRootValue(ctx context.Context) (doltdb.RootValue, error) { 1647 return r.rv, nil 1648 } 1649 1650 func (r rootish) HashOf() (hash.Hash, error) { 1651 return hash.Hash{}, nil 1652 }