@@ -125,9 +125,11 @@ type revertibleTestCase struct {
125
125
fromSchema string
126
126
toSchema string
127
127
// expectProblems bool
128
+ removedForeignKeyNames string
128
129
removedUniqueKeyNames string
129
130
droppedNoDefaultColumnNames string
130
131
expandedColumnNames string
132
+ onlyIfFKOnlineDDLPossible bool
131
133
}
132
134
133
135
func TestMain (m * testing.M ) {
@@ -218,6 +220,25 @@ func TestSchemaChange(t *testing.T) {
218
220
219
221
func testRevertible (t * testing.T ) {
220
222
223
+ fkOnlineDDLPossible := false
224
+ t .Run ("check 'rename_table_preserve_foreign_key' variable" , func (t * testing.T ) {
225
+ // Online DDL is not possible on vanilla MySQL 8.0 for reasons described in https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/.
226
+ // However, Online DDL is made possible in via these changes: https://github.com/planetscale/mysql-server/commit/bb777e3e86387571c044fb4a2beb4f8c60462ced
227
+ // as part of https://github.com/planetscale/mysql-server/releases/tag/8.0.34-ps1.
228
+ // Said changes introduce a new global/session boolean variable named 'rename_table_preserve_foreign_key'. It defaults 'false'/0 for backwards compatibility.
229
+ // When enabled, a `RENAME TABLE` to a FK parent "pins" the children's foreign keys to the table name rather than the table pointer. Which means after the RENAME,
230
+ // the children will point to the newly instated table rather than the original, renamed table.
231
+ // (Note: this applies to a particular type of RENAME where we swap tables, see the above blog post).
232
+ // For FK children, the MySQL changes simply ignore any Vitess-internal table.
233
+ //
234
+ // In this stress test, we enable Online DDL if the variable 'rename_table_preserve_foreign_key' is present. The Online DDL mechanism will in turn
235
+ // query for this variable, and manipulate it, when starting the migration and when cutting over.
236
+ rs , err := shards [0 ].Vttablets [0 ].VttabletProcess .QueryTablet ("show global variables like 'rename_table_preserve_foreign_key'" , keyspaceName , false )
237
+ require .NoError (t , err )
238
+ fkOnlineDDLPossible = len (rs .Rows ) > 0
239
+ t .Logf ("MySQL support for 'rename_table_preserve_foreign_key': %v" , fkOnlineDDLPossible )
240
+ })
241
+
221
242
var testCases = []revertibleTestCase {
222
243
{
223
244
name : "identical schemas" ,
@@ -253,6 +274,20 @@ func testRevertible(t *testing.T) {
253
274
toSchema : `id int primary key, i1 int default null, unique key i1_uidx(i1)` ,
254
275
removedUniqueKeyNames : `` ,
255
276
},
277
+ {
278
+ name : "removed foreign key" ,
279
+ fromSchema : "id int primary key, i int, constraint some_fk_1 foreign key (i) references parent (id) on delete cascade" ,
280
+ toSchema : "id int primary key, i int" ,
281
+ removedForeignKeyNames : "some_fk_1" ,
282
+ onlyIfFKOnlineDDLPossible : true ,
283
+ },
284
+
285
+ {
286
+ name : "renamed foreign key" ,
287
+ fromSchema : "id int primary key, i int, constraint f1 foreign key (i) references parent (id) on delete cascade" ,
288
+ toSchema : "id int primary key, i int, constraint f2 foreign key (i) references parent (id) on delete cascade" ,
289
+ onlyIfFKOnlineDDLPossible : true ,
290
+ },
256
291
{
257
292
name : "remove column without default" ,
258
293
fromSchema : `id int primary key, i1 int not null` ,
@@ -344,24 +379,30 @@ func testRevertible(t *testing.T) {
344
379
dropTableStatement = `
345
380
DROP TABLE onlineddl_test
346
381
`
347
- tableName = "onlineddl_test"
348
- ddlStrategy = "online --declarative --allow-zero-in-date"
382
+ tableName = "onlineddl_test"
383
+ ddlStrategy = "online --declarative --allow-zero-in-date --unsafe-allow-foreign-keys"
384
+ createParentTable = "create table parent (id int primary key)"
349
385
)
350
386
387
+ onlineddl .VtgateExecQuery (t , & vtParams , createParentTable , "" )
388
+
351
389
removeBackticks := func (s string ) string {
352
390
return strings .Replace (s , "`" , "" , - 1 )
353
391
}
354
392
355
393
for _ , testcase := range testCases {
356
394
t .Run (testcase .name , func (t * testing.T ) {
395
+ if testcase .onlyIfFKOnlineDDLPossible && ! fkOnlineDDLPossible {
396
+ t .Skipf ("skipped because backing database does not support 'rename_table_preserve_foreign_key'" )
397
+ return
398
+ }
357
399
358
400
t .Run ("ensure table dropped" , func (t * testing.T ) {
359
401
// A preparation step, to clean up anything from the previous test case
360
402
uuid := testOnlineDDLStatement (t , dropTableStatement , ddlStrategy , "vtgate" , tableName , "" )
361
403
onlineddl .CheckMigrationStatus (t , & vtParams , shards , uuid , schema .OnlineDDLStatusComplete )
362
404
checkTable (t , tableName , false )
363
405
})
364
-
365
406
t .Run ("create from-table" , func (t * testing.T ) {
366
407
// A preparation step, to re-create the base table
367
408
fromStatement := fmt .Sprintf (createTableWrapper , testcase .fromSchema )
@@ -382,17 +423,56 @@ func testRevertible(t *testing.T) {
382
423
rs := onlineddl .ReadMigrations (t , & vtParams , uuid )
383
424
require .NotNil (t , rs )
384
425
for _ , row := range rs .Named ().Rows {
426
+ removedForeignKeyNames := row .AsString ("removed_foreign_key_names" , "" )
385
427
removedUniqueKeyNames := row .AsString ("removed_unique_key_names" , "" )
386
428
droppedNoDefaultColumnNames := row .AsString ("dropped_no_default_column_names" , "" )
387
429
expandedColumnNames := row .AsString ("expanded_column_names" , "" )
388
430
431
+ assert .Equal (t , testcase .removedForeignKeyNames , removeBackticks (removedForeignKeyNames ))
389
432
assert .Equal (t , testcase .removedUniqueKeyNames , removeBackticks (removedUniqueKeyNames ))
390
433
assert .Equal (t , testcase .droppedNoDefaultColumnNames , removeBackticks (droppedNoDefaultColumnNames ))
391
434
assert .Equal (t , testcase .expandedColumnNames , removeBackticks (expandedColumnNames ))
392
435
}
393
436
})
394
437
})
395
438
}
439
+
440
+ t .Run ("drop fk child table" , func (t * testing.T ) {
441
+ t .Run ("ensure table dropped" , func (t * testing.T ) {
442
+ // A preparation step, to clean up anything from the previous test case
443
+ uuid := testOnlineDDLStatement (t , dropTableStatement , ddlStrategy , "vtgate" , tableName , "" )
444
+ onlineddl .CheckMigrationStatus (t , & vtParams , shards , uuid , schema .OnlineDDLStatusComplete )
445
+ checkTable (t , tableName , false )
446
+ })
447
+ t .Run ("create child table" , func (t * testing.T ) {
448
+ fromStatement := fmt .Sprintf (createTableWrapper , "id int primary key, i int, constraint some_fk_2 foreign key (i) references parent (id) on delete cascade" )
449
+ uuid := testOnlineDDLStatement (t , fromStatement , ddlStrategy , "vtgate" , tableName , "" )
450
+ onlineddl .CheckMigrationStatus (t , & vtParams , shards , uuid , schema .OnlineDDLStatusComplete )
451
+ checkTable (t , tableName , true )
452
+ })
453
+ var uuid string
454
+ t .Run ("drop" , func (t * testing.T ) {
455
+ uuid = testOnlineDDLStatement (t , dropTableStatement , ddlStrategy , "vtgate" , tableName , "" )
456
+ onlineddl .CheckMigrationStatus (t , & vtParams , shards , uuid , schema .OnlineDDLStatusComplete )
457
+ checkTable (t , tableName , false )
458
+ })
459
+ t .Run ("check migration" , func (t * testing.T ) {
460
+ // All right, the actual test
461
+ rs := onlineddl .ReadMigrations (t , & vtParams , uuid )
462
+ require .NotNil (t , rs )
463
+ for _ , row := range rs .Named ().Rows {
464
+ removedForeignKeyNames := row .AsString ("removed_foreign_key_names" , "" )
465
+ removedUniqueKeyNames := row .AsString ("removed_unique_key_names" , "" )
466
+ droppedNoDefaultColumnNames := row .AsString ("dropped_no_default_column_names" , "" )
467
+ expandedColumnNames := row .AsString ("expanded_column_names" , "" )
468
+
469
+ assert .Equal (t , "some_fk_2" , removeBackticks (removedForeignKeyNames ))
470
+ assert .Equal (t , "" , removeBackticks (removedUniqueKeyNames ))
471
+ assert .Equal (t , "" , removeBackticks (droppedNoDefaultColumnNames ))
472
+ assert .Equal (t , "" , removeBackticks (expandedColumnNames ))
473
+ }
474
+ })
475
+ })
396
476
}
397
477
398
478
func testRevert (t * testing.T ) {
@@ -964,6 +1044,8 @@ func testRevert(t *testing.T) {
964
1044
assert .Empty (t , specialPlan )
965
1045
assert .NotEmpty (t , artifacts )
966
1046
}
1047
+ removedForeignKeyNames := row .AsString ("removed_foreign_key_names" , "" )
1048
+ assert .Empty (t , removedForeignKeyNames )
967
1049
})
968
1050
t .Run ("INSTANT DDL: fail revert" , func (t * testing.T ) {
969
1051
uuid := testRevertMigration (t , uuids [len (uuids )- 1 ], ddlStrategy )
0 commit comments