@@ -10,6 +10,7 @@ import (
10
10
gosql "database/sql"
11
11
"strings"
12
12
"testing"
13
+ "time"
13
14
14
15
"github.com/stretchr/testify/require"
15
16
"github.com/stretchr/testify/suite"
@@ -100,6 +101,7 @@ func TestApplierBuildDMLEventQuery(t *testing.T) {
100
101
columnValues := sql .ToColumnValues ([]interface {}{123456 , 42 })
101
102
102
103
migrationContext := base .NewMigrationContext ()
104
+ migrationContext .DatabaseName = "test"
103
105
migrationContext .OriginalTableName = "test"
104
106
migrationContext .OriginalTableColumns = columns
105
107
migrationContext .SharedColumns = columns
@@ -110,6 +112,7 @@ func TestApplierBuildDMLEventQuery(t *testing.T) {
110
112
}
111
113
112
114
applier := NewApplier (migrationContext )
115
+ applier .prepareQueries ()
113
116
114
117
t .Run ("delete" , func (t * testing.T ) {
115
118
binlogEvent := & binlog.BinlogDMLEvent {
@@ -199,6 +202,30 @@ type ApplierTestSuite struct {
199
202
mysqlContainer testcontainers.Container
200
203
}
201
204
205
+ func (suite * ApplierTestSuite ) getConnectionConfig (ctx context.Context ) (* mysql.ConnectionConfig , error ) {
206
+ host , err := suite .mysqlContainer .ContainerIP (ctx )
207
+ if err != nil {
208
+ return nil , err
209
+ }
210
+
211
+ config := mysql .NewConnectionConfig ()
212
+ config .Key .Hostname = host
213
+ config .Key .Port = 3306
214
+ config .User = "root"
215
+ config .Password = "root-password"
216
+
217
+ return config , nil
218
+ }
219
+
220
+ func (suite * ApplierTestSuite ) getDb (ctx context.Context ) (* gosql.DB , error ) {
221
+ host , err := suite .mysqlContainer .ContainerIP (ctx )
222
+ if err != nil {
223
+ return nil , err
224
+ }
225
+
226
+ return gosql .Open ("mysql" , "root:root-password@tcp(" + host + ":3306)/test" )
227
+ }
228
+
202
229
func (suite * ApplierTestSuite ) SetupSuite () {
203
230
ctx := context .Background ()
204
231
req := testcontainers.ContainerRequest {
@@ -229,7 +256,7 @@ func (suite *ApplierTestSuite) SetupTest() {
229
256
suite .Require ().NoError (err )
230
257
suite .Require ().Equalf (0 , rc , "failed to created database: expected exit code 0, got %d" , rc )
231
258
232
- rc , _ , err = suite .mysqlContainer .Exec (ctx , []string {"mysql" , "-uroot" , "-proot-password" , "-e" , "CREATE TABLE test.testing (id INT, item_id INT);" })
259
+ rc , _ , err = suite .mysqlContainer .Exec (ctx , []string {"mysql" , "-uroot" , "-proot-password" , "-e" , "CREATE TABLE test.testing (id INT, item_id INT, PRIMARY KEY (id) );" })
233
260
suite .Require ().NoError (err )
234
261
suite .Require ().Equalf (0 , rc , "failed to created table: expected exit code 0, got %d" , rc )
235
262
}
@@ -245,15 +272,11 @@ func (suite *ApplierTestSuite) TearDownTest() {
245
272
func (suite * ApplierTestSuite ) TestInitDBConnections () {
246
273
ctx := context .Background ()
247
274
248
- host , err := suite .mysqlContainer . ContainerIP (ctx )
275
+ connectionConfig , err := suite .getConnectionConfig (ctx )
249
276
suite .Require ().NoError (err )
250
277
251
278
migrationContext := base .NewMigrationContext ()
252
- migrationContext .ApplierConnectionConfig = mysql .NewConnectionConfig ()
253
- migrationContext .ApplierConnectionConfig .Key .Hostname = host
254
- migrationContext .ApplierConnectionConfig .Key .Port = 3306
255
- migrationContext .ApplierConnectionConfig .User = "root"
256
- migrationContext .ApplierConnectionConfig .Password = "root-password"
279
+ migrationContext .ApplierConnectionConfig = connectionConfig
257
280
migrationContext .DatabaseName = "test"
258
281
migrationContext .OriginalTableName = "testing"
259
282
migrationContext .SetConnectionConfig ("innodb" )
@@ -274,24 +297,25 @@ func (suite *ApplierTestSuite) TestInitDBConnections() {
274
297
func (suite * ApplierTestSuite ) TestApplyDMLEventQueries () {
275
298
ctx := context .Background ()
276
299
277
- host , err := suite .mysqlContainer . ContainerIP (ctx )
300
+ connectionConfig , err := suite .getConnectionConfig (ctx )
278
301
suite .Require ().NoError (err )
279
302
280
303
migrationContext := base .NewMigrationContext ()
281
- migrationContext .ApplierConnectionConfig = mysql .NewConnectionConfig ()
282
- migrationContext .ApplierConnectionConfig .Key .Hostname = host
283
- migrationContext .ApplierConnectionConfig .Key .Port = 3306
284
- migrationContext .ApplierConnectionConfig .User = "root"
285
- migrationContext .ApplierConnectionConfig .Password = "root-password"
304
+ migrationContext .ApplierConnectionConfig = connectionConfig
286
305
migrationContext .DatabaseName = "test"
287
306
migrationContext .OriginalTableName = "testing"
288
307
migrationContext .SetConnectionConfig ("innodb" )
289
308
290
309
migrationContext .OriginalTableColumns = sql .NewColumnList ([]string {"id" , "item_id" })
291
310
migrationContext .SharedColumns = sql .NewColumnList ([]string {"id" , "item_id" })
292
311
migrationContext .MappedSharedColumns = sql .NewColumnList ([]string {"id" , "item_id" })
312
+ migrationContext .UniqueKey = & sql.UniqueKey {
313
+ Name : "primary_key" ,
314
+ Columns : * sql .NewColumnList ([]string {"id" }),
315
+ }
293
316
294
317
applier := NewApplier (migrationContext )
318
+ suite .Require ().NoError (applier .prepareQueries ())
295
319
defer applier .Teardown ()
296
320
297
321
err = applier .InitDBConnections ()
@@ -313,7 +337,7 @@ func (suite *ApplierTestSuite) TestApplyDMLEventQueries() {
313
337
suite .Require ().NoError (err )
314
338
315
339
// Check that the row was inserted
316
- db , err := gosql . Open ( "mysql" , "root:root-password@tcp(" + host + ":3306)/test" )
340
+ db , err := suite . getDb ( ctx )
317
341
suite .Require ().NoError (err )
318
342
defer db .Close ()
319
343
@@ -340,15 +364,11 @@ func (suite *ApplierTestSuite) TestApplyDMLEventQueries() {
340
364
func (suite * ApplierTestSuite ) TestValidateOrDropExistingTables () {
341
365
ctx := context .Background ()
342
366
343
- host , err := suite .mysqlContainer . ContainerIP (ctx )
367
+ connectionConfig , err := suite .getConnectionConfig (ctx )
344
368
suite .Require ().NoError (err )
345
369
346
370
migrationContext := base .NewMigrationContext ()
347
- migrationContext .ApplierConnectionConfig = mysql .NewConnectionConfig ()
348
- migrationContext .ApplierConnectionConfig .Key .Hostname = host
349
- migrationContext .ApplierConnectionConfig .Key .Port = 3306
350
- migrationContext .ApplierConnectionConfig .User = "root"
351
- migrationContext .ApplierConnectionConfig .Password = "root-password"
371
+ migrationContext .ApplierConnectionConfig = connectionConfig
352
372
migrationContext .DatabaseName = "test"
353
373
migrationContext .OriginalTableName = "testing"
354
374
migrationContext .SetConnectionConfig ("innodb" )
@@ -367,6 +387,140 @@ func (suite *ApplierTestSuite) TestValidateOrDropExistingTables() {
367
387
suite .Require ().NoError (err )
368
388
}
369
389
390
+ func (suite * ApplierTestSuite ) TestApplyIterationInsertQuery () {
391
+ ctx := context .Background ()
392
+
393
+ connectionConfig , err := suite .getConnectionConfig (ctx )
394
+ suite .Require ().NoError (err )
395
+
396
+ migrationContext := base .NewMigrationContext ()
397
+ migrationContext .ApplierConnectionConfig = connectionConfig
398
+ migrationContext .DatabaseName = "test"
399
+ migrationContext .OriginalTableName = "testing"
400
+ migrationContext .ChunkSize = 10
401
+ migrationContext .SetConnectionConfig ("innodb" )
402
+
403
+ db , err := suite .getDb (ctx )
404
+ suite .Require ().NoError (err )
405
+ defer db .Close ()
406
+
407
+ _ , err = db .Exec ("CREATE TABLE test._testing_gho (id INT, item_id INT, PRIMARY KEY (id))" )
408
+ suite .Require ().NoError (err )
409
+
410
+ // Insert some test values
411
+ for i := 1 ; i <= 10 ; i ++ {
412
+ _ , err = db .Exec ("INSERT INTO test.testing (id, item_id) VALUES (?, ?)" , i , i )
413
+ suite .Require ().NoError (err )
414
+ }
415
+
416
+ migrationContext .SharedColumns = sql .NewColumnList ([]string {"id" , "item_id" })
417
+ migrationContext .MappedSharedColumns = sql .NewColumnList ([]string {"id" , "item_id" })
418
+ migrationContext .UniqueKey = & sql.UniqueKey {
419
+ Name : "PRIMARY" ,
420
+ Columns : * sql .NewColumnList ([]string {"id" }),
421
+ }
422
+
423
+ migrationContext .MigrationIterationRangeMinValues = sql .ToColumnValues ([]interface {}{1 })
424
+ migrationContext .MigrationIterationRangeMaxValues = sql .ToColumnValues ([]interface {}{10 })
425
+
426
+ applier := NewApplier (migrationContext )
427
+ defer applier .Teardown ()
428
+
429
+ err = applier .InitDBConnections ()
430
+ suite .Require ().NoError (err )
431
+
432
+ chunkSize , rowsAffected , duration , err := applier .ApplyIterationInsertQuery ()
433
+ suite .Require ().NoError (err )
434
+
435
+ suite .Require ().Equal (migrationContext .ChunkSize , chunkSize )
436
+ suite .Require ().Equal (int64 (10 ), rowsAffected )
437
+ suite .Require ().Greater (duration , time .Duration (0 ))
438
+
439
+ // Check that the rows were inserted
440
+ rows , err := db .Query ("SELECT * FROM test._testing_gho" )
441
+ suite .Require ().NoError (err )
442
+ defer rows .Close ()
443
+
444
+ var count , id , item_id int
445
+ for rows .Next () {
446
+ err = rows .Scan (& id , & item_id )
447
+ suite .Require ().NoError (err )
448
+ count += 1
449
+ }
450
+ suite .Require ().NoError (rows .Err ())
451
+
452
+ suite .Require ().Equal (10 , count )
453
+ }
454
+
455
+ func (suite * ApplierTestSuite ) TestApplyIterationInsertQueryFailsFastWhenSelectingLockedRows () {
456
+ ctx := context .Background ()
457
+
458
+ connectionConfig , err := suite .getConnectionConfig (ctx )
459
+ suite .Require ().NoError (err )
460
+
461
+ migrationContext := base .NewMigrationContext ()
462
+ migrationContext .ApplierConnectionConfig = connectionConfig
463
+ migrationContext .DatabaseName = "test"
464
+ migrationContext .OriginalTableName = "testing"
465
+ migrationContext .ChunkSize = 10
466
+ migrationContext .TableEngine = "innodb"
467
+ migrationContext .SetConnectionConfig ("innodb" )
468
+
469
+ db , err := suite .getDb (ctx )
470
+ suite .Require ().NoError (err )
471
+ defer db .Close ()
472
+
473
+ _ , err = db .Exec ("CREATE TABLE test._testing_gho (id INT, item_id INT, PRIMARY KEY (id))" )
474
+ suite .Require ().NoError (err )
475
+
476
+ // Insert some test values
477
+ for i := 1 ; i <= 10 ; i ++ {
478
+ _ , err = db .Exec ("INSERT INTO test.testing (id, item_id) VALUES (?, ?)" , i , i )
479
+ suite .Require ().NoError (err )
480
+ }
481
+
482
+ migrationContext .SharedColumns = sql .NewColumnList ([]string {"id" , "item_id" })
483
+ migrationContext .MappedSharedColumns = sql .NewColumnList ([]string {"id" , "item_id" })
484
+ migrationContext .UniqueKey = & sql.UniqueKey {
485
+ Name : "PRIMARY" ,
486
+ Columns : * sql .NewColumnList ([]string {"id" }),
487
+ }
488
+
489
+ migrationContext .MigrationIterationRangeMinValues = sql .ToColumnValues ([]interface {}{1 })
490
+ migrationContext .MigrationIterationRangeMaxValues = sql .ToColumnValues ([]interface {}{10 })
491
+
492
+ applier := NewApplier (migrationContext )
493
+ defer applier .Teardown ()
494
+
495
+ err = applier .InitDBConnections ()
496
+ suite .Require ().NoError (err )
497
+
498
+ // Lock one of the rows
499
+ tx , err := db .Begin ()
500
+ suite .Require ().NoError (err )
501
+ defer func () {
502
+ suite .Require ().NoError (tx .Rollback ())
503
+ }()
504
+
505
+ _ , err = tx .Exec ("SELECT * FROM test.testing WHERE id = 5 FOR UPDATE" )
506
+ suite .Require ().NoError (err )
507
+
508
+ chunkSize , rowsAffected , duration , err := applier .ApplyIterationInsertQuery ()
509
+ suite .Require ().Error (err )
510
+ suite .Require ().EqualError (err , "Error 3572 (HY000): Statement aborted because lock(s) could not be acquired immediately and NOWAIT is set." )
511
+
512
+ suite .Require ().Equal (migrationContext .ChunkSize , chunkSize )
513
+ suite .Require ().Equal (int64 (0 ), rowsAffected )
514
+ suite .Require ().Equal (time .Duration (0 ), duration )
515
+
516
+ // Check that the no rows were inserted
517
+ var count int
518
+ err = db .QueryRow ("SELECT COUNT(*) FROM test._testing_gho" ).Scan (& count )
519
+ suite .Require ().NoError (err )
520
+
521
+ suite .Require ().Equal (0 , count )
522
+ }
523
+
370
524
func TestApplier (t * testing.T ) {
371
525
suite .Run (t , new (ApplierTestSuite ))
372
526
}
0 commit comments