39
39
package tests
40
40
41
41
import (
42
+ "fmt"
42
43
"regexp"
43
44
"testing"
44
45
@@ -52,7 +53,6 @@ import (
52
53
)
53
54
54
55
func TestUpsert (t * testing.T ) {
55
- t .Skip ()
56
56
lang := Language {Code : "upsert" , Name : "Upsert" }
57
57
if err := DB .Clauses (clause.OnConflict {DoNothing : true }).Create (& lang ).Error ; err != nil {
58
58
t .Fatalf ("failed to upsert, got %v" , err )
@@ -64,10 +64,10 @@ func TestUpsert(t *testing.T) {
64
64
}
65
65
66
66
var langs []Language
67
- if err := DB .Find (& langs , "code = ?" , lang .Code ).Error ; err != nil {
68
- t .Errorf ("no error should happen when find languages with code, but got %v" , err )
67
+ if err := DB .Find (& langs , "\" code\" = ?" , lang .Code ).Error ; err != nil {
68
+ t .Fatalf ("no error should happen when find languages with code, but got %v" , err )
69
69
} else if len (langs ) != 1 {
70
- t .Errorf ("should only find only 1 languages, but got %+v" , langs )
70
+ t .Fatalf ("should only find only 1 languages, but got %+v" , langs )
71
71
}
72
72
73
73
lang3 := Language {Code : "upsert" , Name : "Upsert" }
@@ -78,12 +78,12 @@ func TestUpsert(t *testing.T) {
78
78
t .Fatalf ("failed to upsert, got %v" , err )
79
79
}
80
80
81
- if err := DB .Find (& langs , "code = ?" , lang .Code ).Error ; err != nil {
82
- t .Errorf ("no error should happen when find languages with code, but got %v" , err )
81
+ if err := DB .Find (& langs , "\" code\" = ?" , lang .Code ).Error ; err != nil {
82
+ t .Fatalf ("no error should happen when find languages with code, but got %v" , err )
83
83
} else if len (langs ) != 1 {
84
- t .Errorf ("should only find only 1 languages, but got %+v" , langs )
84
+ t .Fatalf ("should only find only 1 languages, but got %+v" , langs )
85
85
} else if langs [0 ].Name != "upsert-new" {
86
- t .Errorf ("should update name on conflict, but got name %+v" , langs [0 ].Name )
86
+ t .Fatalf ("should update name on conflict, but got name %+v" , langs [0 ].Name )
87
87
}
88
88
89
89
lang = Language {Code : "upsert" , Name : "Upsert-Newname" }
@@ -92,27 +92,25 @@ func TestUpsert(t *testing.T) {
92
92
}
93
93
94
94
var result Language
95
- if err := DB .Find (& result , "code = ?" , lang .Code ).Error ; err != nil || result .Name != lang .Name {
95
+ if err := DB .Find (& result , "\" code\" = ?" , lang .Code ).Error ; err != nil || result .Name != lang .Name {
96
96
t .Fatalf ("failed to upsert, got name %v" , result .Name )
97
97
}
98
98
99
- if name := DB .Dialector .Name (); name != "sqlserver" {
100
- type RestrictedLanguage struct {
101
- Code string `gorm:"primarykey"`
102
- Name string
103
- Lang string `gorm:"<-:create"`
104
- }
99
+ type RestrictedLanguage struct {
100
+ Code string `gorm:"primarykey"`
101
+ Name string
102
+ Lang string `gorm:"<-:create"`
103
+ }
105
104
106
- r := DB .Session (& gorm.Session {DryRun : true }).Clauses (clause.OnConflict {UpdateAll : true }).Create (& RestrictedLanguage {Code : "upsert_code" , Name : "upsert_name" , Lang : "upsert_lang" })
107
- if ! regexp .MustCompile (`INTO .restricted_languages. .*\(.code.,.name.,.lang.\) .* (SET|UPDATE) .name.=.*.name.\W*$` ).MatchString (r .Statement .SQL .String ()) {
108
- t .Errorf ("Table with escape character, got %v" , r .Statement .SQL .String ())
109
- }
105
+ r := DB .Session (& gorm.Session {DryRun : true }).Clauses (clause.OnConflict {UpdateAll : true }).Create (& RestrictedLanguage {Code : "upsert_code" , Name : "upsert_name" , Lang : "upsert_lang" })
106
+ if ! regexp .MustCompile (`MERGE INTO "restricted_languages".*WHEN MATCHED THEN UPDATE SET "name"="excluded"."name".*INSERT \("code","name","lang"\)` ).MatchString (r .Statement .SQL .String ()) {
107
+ t .Fatalf ("Table with escape character, got %v" , r .Statement .SQL .String ())
110
108
}
111
109
112
110
user := * GetUser ("upsert_on_conflict" , Config {})
113
111
user .Age = 20
114
112
if err := DB .Create (& user ).Error ; err != nil {
115
- t .Errorf ("failed to create user, got error %v" , err )
113
+ t .Fatalf ("failed to create user, got error %v" , err )
116
114
}
117
115
118
116
var user2 User
@@ -124,6 +122,8 @@ func TestUpsert(t *testing.T) {
124
122
} else {
125
123
var user3 User
126
124
DB .First (& user3 , user .ID )
125
+ fmt .Printf ("%d\n " , user3 .UpdatedAt .UnixNano ())
126
+ fmt .Printf ("%d\n " , user2 .UpdatedAt .UnixNano ())
127
127
if user3 .UpdatedAt .UnixNano () == user2 .UpdatedAt .UnixNano () {
128
128
t .Fatalf ("failed to update user's updated_at, old: %v, new: %v" , user2 .UpdatedAt , user3 .UpdatedAt )
129
129
}
@@ -369,3 +369,227 @@ func TestUpdateWithMissWhere(t *testing.T) {
369
369
t .Fatalf ("invalid updating SQL, got %v" , tx .Statement .SQL .String ())
370
370
}
371
371
}
372
+
373
+ type CompositeLang struct {
374
+ Code string `gorm:"primaryKey;size:100"`
375
+ Lang string `gorm:"primaryKey;size:10"`
376
+ Name string
377
+ }
378
+
379
+ func TestUpsertCompositePK (t * testing.T ) {
380
+ langs := []CompositeLang {
381
+ {Code : "c1" , Lang : "en" , Name : "English" },
382
+ {Code : "c1" , Lang : "fr" , Name : "French" },
383
+ }
384
+
385
+ DB .Migrator ().DropTable (& CompositeLang {})
386
+ DB .Migrator ().AutoMigrate (& CompositeLang {})
387
+
388
+ if err := DB .Create (& langs ).Error ; err != nil {
389
+ t .Fatalf ("failed to insert composite PK: %v" , err )
390
+ }
391
+
392
+ for i := range langs {
393
+ langs [i ].Name = langs [i ].Name + "_updated"
394
+ }
395
+
396
+ if err := DB .Clauses (clause.OnConflict {
397
+ UpdateAll : true ,
398
+ }).Create (& langs ).Error ; err != nil {
399
+ t .Fatalf ("failed to upsert composite PK: %v" , err )
400
+ }
401
+
402
+ for _ , expected := range langs {
403
+ var result CompositeLang
404
+ if err := DB .First (& result , "\" code\" = ? AND \" lang\" = ?" , expected .Code , expected .Lang ).Error ; err != nil {
405
+ t .Fatalf ("failed to fetch row for %+v: %v" , expected , err )
406
+ }
407
+ if result .Name != expected .Name {
408
+ t .Fatalf ("expected %v, got %v" , expected .Name , result .Name )
409
+ }
410
+ }
411
+
412
+ DB .Migrator ().DropTable (& CompositeLang {})
413
+ }
414
+
415
+ func TestUpsertPrimaryKeyNotUpdated (t * testing.T ) {
416
+ lang := Language {Code : "pk1" , Name : "Name1" }
417
+ DB .Create (& lang )
418
+
419
+ lang .Code = "pk2" // try changing PK
420
+ DB .Clauses (clause.OnConflict {UpdateAll : true }).Create (& lang )
421
+
422
+ var result Language
423
+ DB .First (& result , "\" code\" = ?" , "pk1" )
424
+ if result .Name != "Name1" {
425
+ t .Fatalf ("expected original row untouched, got %v" , result )
426
+ }
427
+ }
428
+
429
+ type LangWithIgnore struct {
430
+ Code string `gorm:"primaryKey"`
431
+ Name string
432
+ Lang string `gorm:"<-:create"` // should not be updated
433
+ }
434
+
435
+ func TestUpsertIgnoreColumn (t * testing.T ) {
436
+ DB .Migrator ().DropTable (& LangWithIgnore {})
437
+ DB .Migrator ().AutoMigrate (& LangWithIgnore {})
438
+ lang := LangWithIgnore {Code : "upsert_ignore" , Name : "OldName" , Lang : "en" }
439
+ DB .Create (& lang )
440
+
441
+ lang .Name = "NewName"
442
+ lang .Lang = "fr"
443
+ DB .Clauses (clause.OnConflict {UpdateAll : true }).Create (& lang )
444
+
445
+ var result LangWithIgnore
446
+ DB .First (& result , "\" code\" = ?" , lang .Code )
447
+ if result .Name != "NewName" {
448
+ t .Fatalf ("expected Name updated, got %v" , result .Name )
449
+ }
450
+ if result .Lang != "en" {
451
+ t .Fatalf ("Lang should not be updated, got %v" , result .Lang )
452
+ }
453
+ DB .Migrator ().DropTable (& LangWithIgnore {})
454
+ }
455
+
456
+ func TestUpsertNullValues (t * testing.T ) {
457
+ lang := Language {Code : "upsert_null" , Name : "" }
458
+ DB .Clauses (clause.OnConflict {UpdateAll : true }).Create (& lang )
459
+
460
+ var result Language
461
+ DB .First (& result , "\" code\" = ?" , lang .Code )
462
+ if result .Name != "" {
463
+ t .Fatalf ("expected empty Name, got %v" , result .Name )
464
+ }
465
+ }
466
+
467
+ func TestUpsertWithNullUnique (t * testing.T ) {
468
+ type NullLang struct {
469
+ Code * string `gorm:"uniqueIndex"`
470
+ Name string
471
+ }
472
+ DB .Migrator ().DropTable (& NullLang {})
473
+ DB .Migrator ().AutoMigrate (& NullLang {})
474
+
475
+ DB .Create (& NullLang {Code : nil , Name : "First" })
476
+
477
+ if err := DB .Clauses (clause.OnConflict {
478
+ Columns : []clause.Column {{Name : "code" }},
479
+ DoUpdates : clause .AssignmentColumns ([]string {"name" }),
480
+ }).Create (& NullLang {Code : nil , Name : "Second" }).Error ; err != nil {
481
+ t .Fatalf ("unexpected error on upsert with NULL: %v" , err )
482
+ }
483
+
484
+ var count int64
485
+ DB .Model (& NullLang {}).Count (& count )
486
+ if count != 2 {
487
+ t .Fatalf ("expected 2 rows due to NULL uniqueness, got %d" , count )
488
+ }
489
+ }
490
+
491
+ func TestUpsertSliceMixed (t * testing.T ) {
492
+ DB .Create (& Language {Code : "m1" , Name : "Old1" })
493
+ langs := []Language {
494
+ {Code : "m1" , Name : "New1" }, // exists
495
+ {Code : "m2" , Name : "New2" }, // new
496
+ }
497
+
498
+ DB .Clauses (clause.OnConflict {UpdateAll : true }).Create (& langs )
499
+
500
+ var l1 , l2 Language
501
+ DB .First (& l1 , "\" code\" = ?" , "m1" )
502
+ DB .First (& l2 , "\" code\" = ?" , "m2" )
503
+ if l1 .Name != "New1" || l2 .Name != "New2" {
504
+ t .Fatalf ("batch mixed upsert failed: %+v, %+v" , l1 , l2 )
505
+ }
506
+ }
507
+
508
+ func TestUpsertWithExpressions (t * testing.T ) {
509
+ lang := Language {Code : "expr1" , Name : "Name1" }
510
+ DB .Create (& lang )
511
+
512
+ DB .Clauses (clause.OnConflict {
513
+ Columns : []clause.Column {{Name : "code" }},
514
+ DoUpdates : clause .Assignments (map [string ]interface {}{
515
+ "name" : gorm .Expr ("UPPER(?)" , "newname" ),
516
+ }),
517
+ }).Create (& lang )
518
+
519
+ var result Language
520
+ DB .First (& result , "\" code\" = ?" , "expr1" )
521
+ if result .Name != "NEWNAME" {
522
+ t .Fatalf ("expected NEWNAME, got %v" , result .Name )
523
+ }
524
+ }
525
+
526
+ func TestUpsertLargeBatch (t * testing.T ) {
527
+ var langs []Language
528
+ for i := 0 ; i < 1000 ; i ++ {
529
+ langs = append (langs , Language {Code : fmt .Sprintf ("lb_%d" , i ), Name : "Name" })
530
+ }
531
+ if err := DB .Clauses (clause.OnConflict {DoNothing : true }).Create (& langs ).Error ; err != nil {
532
+ t .Fatalf ("failed large batch insert: %v" , err )
533
+ }
534
+ }
535
+
536
+ func TestUpsertFromSubquery (t * testing.T ) {
537
+ DB .Migrator ().DropTable (& Language {})
538
+ if err := DB .AutoMigrate (& Language {}); err != nil {
539
+ t .Fatalf ("failed to migrate: %v" , err )
540
+ }
541
+
542
+ initial := []Language {
543
+ {Code : "en" , Name : "English" },
544
+ {Code : "fr" , Name : "French - Old" }, // Will be updated
545
+ {Code : "es" , Name : "Spanish - Old" }, // Will be updated
546
+ }
547
+ if err := DB .Create (& initial ).Error ; err != nil {
548
+ t .Fatalf ("failed to seed: %v" , err )
549
+ }
550
+
551
+ updates := []Language {
552
+ {Code : "fr" , Name : "French - Updated" },
553
+ {Code : "es" , Name : "Spanish - Updated" },
554
+ {Code : "de" , Name : "German" }, // New record
555
+ }
556
+
557
+ for _ , update := range updates {
558
+ err := DB .Clauses (clause.OnConflict {
559
+ Columns : []clause.Column {{Name : "code" }},
560
+ DoUpdates : clause .AssignmentColumns ([]string {"name" }),
561
+ }).Create (& update ).Error
562
+
563
+ if err != nil {
564
+ t .Fatalf ("failed upsert: %v" , err )
565
+ }
566
+ }
567
+
568
+ var results []Language
569
+ if err := DB .Order ("\" code\" " ).Find (& results ).Error ; err != nil {
570
+ t .Fatalf ("failed to query results: %v" , err )
571
+ }
572
+
573
+ expected := []Language {
574
+ {Code : "de" , Name : "German" }, // inserted
575
+ {Code : "en" , Name : "English" }, // unchanged
576
+ {Code : "es" , Name : "Spanish - Updated" }, // updated
577
+ {Code : "fr" , Name : "French - Updated" }, // updated
578
+ }
579
+
580
+ if len (results ) != len (expected ) {
581
+ t .Errorf ("expected %d rows, got %d" , len (expected ), len (results ))
582
+ }
583
+
584
+ for i := range expected {
585
+ if i >= len (results ) {
586
+ t .Errorf ("missing row %d: expected (%s, %s)" , i , expected [i ].Code , expected [i ].Name )
587
+ continue
588
+ }
589
+ if results [i ].Code != expected [i ].Code || results [i ].Name != expected [i ].Name {
590
+ t .Errorf ("row %d mismatch: expected (%s, %s), got (%s, %s)" ,
591
+ i , expected [i ].Code , expected [i ].Name ,
592
+ results [i ].Code , results [i ].Name )
593
+ }
594
+ }
595
+ }
0 commit comments