2020-05-23 19:52:25 +03:00
|
|
|
|
package tests_test
|
|
|
|
|
|
|
|
|
|
import (
|
2024-04-25 15:21:03 +03:00
|
|
|
|
"context"
|
2020-09-04 14:02:37 +03:00
|
|
|
|
"encoding/json"
|
|
|
|
|
"regexp"
|
2020-05-23 20:16:08 +03:00
|
|
|
|
"sort"
|
2020-05-23 19:52:25 +03:00
|
|
|
|
"strconv"
|
2020-11-27 09:32:20 +03:00
|
|
|
|
"sync"
|
2020-05-23 19:52:25 +03:00
|
|
|
|
"testing"
|
2024-04-25 15:21:03 +03:00
|
|
|
|
"time"
|
2024-03-09 16:27:19 +03:00
|
|
|
|
|
2020-08-25 13:59:19 +03:00
|
|
|
|
"gorm.io/gorm"
|
2020-06-02 04:16:07 +03:00
|
|
|
|
"gorm.io/gorm/clause"
|
2020-06-02 05:34:50 +03:00
|
|
|
|
. "gorm.io/gorm/utils/tests"
|
2020-05-23 19:52:25 +03:00
|
|
|
|
)
|
|
|
|
|
|
2020-06-20 12:21:01 +03:00
|
|
|
|
func TestPreloadWithAssociations(t *testing.T) {
|
2022-01-06 10:02:53 +03:00
|
|
|
|
user := *GetUser("preload_with_associations", Config{
|
2020-06-20 12:21:01 +03:00
|
|
|
|
Account: true,
|
|
|
|
|
Pets: 2,
|
|
|
|
|
Toys: 3,
|
|
|
|
|
Company: true,
|
|
|
|
|
Manager: true,
|
|
|
|
|
Team: 4,
|
|
|
|
|
Languages: 3,
|
|
|
|
|
Friends: 1,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err := DB.Create(&user).Error; err != nil {
|
|
|
|
|
t.Fatalf("errors happened when create: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CheckUser(t, user, user)
|
|
|
|
|
|
|
|
|
|
var user2 User
|
|
|
|
|
DB.Preload(clause.Associations).Find(&user2, "id = ?", user.ID)
|
|
|
|
|
CheckUser(t, user2, user)
|
2020-09-01 08:48:37 +03:00
|
|
|
|
|
2022-01-06 10:02:53 +03:00
|
|
|
|
user3 := *GetUser("preload_with_associations_new", Config{
|
2020-09-01 08:48:37 +03:00
|
|
|
|
Account: true,
|
|
|
|
|
Pets: 2,
|
|
|
|
|
Toys: 3,
|
|
|
|
|
Company: true,
|
|
|
|
|
Manager: true,
|
|
|
|
|
Team: 4,
|
|
|
|
|
Languages: 3,
|
|
|
|
|
Friends: 1,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
DB.Preload(clause.Associations).Find(&user3, "id = ?", user.ID)
|
|
|
|
|
CheckUser(t, user3, user)
|
2020-06-20 12:21:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 19:52:25 +03:00
|
|
|
|
func TestNestedPreload(t *testing.T) {
|
2022-01-06 10:02:53 +03:00
|
|
|
|
user := *GetUser("nested_preload", Config{Pets: 2})
|
2020-05-23 19:52:25 +03:00
|
|
|
|
|
|
|
|
|
for idx, pet := range user.Pets {
|
|
|
|
|
pet.Toy = Toy{Name: "toy_nested_preload_" + strconv.Itoa(idx+1)}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := DB.Create(&user).Error; err != nil {
|
|
|
|
|
t.Fatalf("errors happened when create: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var user2 User
|
|
|
|
|
DB.Preload("Pets.Toy").Find(&user2, "id = ?", user.ID)
|
|
|
|
|
CheckUser(t, user2, user)
|
2021-03-14 05:18:43 +03:00
|
|
|
|
|
|
|
|
|
var user3 User
|
|
|
|
|
DB.Preload(clause.Associations+"."+clause.Associations).Find(&user3, "id = ?", user.ID)
|
|
|
|
|
CheckUser(t, user3, user)
|
2021-04-15 05:37:05 +03:00
|
|
|
|
|
|
|
|
|
var user4 *User
|
|
|
|
|
DB.Preload("Pets.Toy").Find(&user4, "id = ?", user.ID)
|
|
|
|
|
CheckUser(t, *user4, user)
|
2020-05-23 19:52:25 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNestedPreloadForSlice(t *testing.T) {
|
2022-01-06 10:02:53 +03:00
|
|
|
|
users := []User{
|
2020-05-23 19:52:25 +03:00
|
|
|
|
*GetUser("slice_nested_preload_1", Config{Pets: 2}),
|
|
|
|
|
*GetUser("slice_nested_preload_2", Config{Pets: 0}),
|
|
|
|
|
*GetUser("slice_nested_preload_3", Config{Pets: 3}),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, user := range users {
|
|
|
|
|
for idx, pet := range user.Pets {
|
|
|
|
|
pet.Toy = Toy{Name: user.Name + "_toy_nested_preload_" + strconv.Itoa(idx+1)}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := DB.Create(&users).Error; err != nil {
|
|
|
|
|
t.Fatalf("errors happened when create: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var userIDs []uint
|
|
|
|
|
for _, user := range users {
|
|
|
|
|
userIDs = append(userIDs, user.ID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var users2 []User
|
|
|
|
|
DB.Preload("Pets.Toy").Find(&users2, "id IN ?", userIDs)
|
|
|
|
|
|
|
|
|
|
for idx, user := range users2 {
|
|
|
|
|
CheckUser(t, user, users[idx])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPreloadWithConds(t *testing.T) {
|
2022-01-06 10:02:53 +03:00
|
|
|
|
users := []User{
|
2020-05-23 20:16:08 +03:00
|
|
|
|
*GetUser("slice_nested_preload_1", Config{Account: true}),
|
|
|
|
|
*GetUser("slice_nested_preload_2", Config{Account: false}),
|
|
|
|
|
*GetUser("slice_nested_preload_3", Config{Account: true}),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := DB.Create(&users).Error; err != nil {
|
|
|
|
|
t.Fatalf("errors happened when create: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var userIDs []uint
|
|
|
|
|
for _, user := range users {
|
|
|
|
|
userIDs = append(userIDs, user.ID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var users2 []User
|
|
|
|
|
DB.Preload("Account", clause.Eq{Column: "number", Value: users[0].Account.Number}).Find(&users2, "id IN ?", userIDs)
|
|
|
|
|
sort.Slice(users2, func(i, j int) bool {
|
|
|
|
|
return users2[i].ID < users2[j].ID
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
for idx, user := range users2[1:2] {
|
|
|
|
|
if user.Account.Number != "" {
|
|
|
|
|
t.Errorf("No account should found for user %v but got %v", idx+2, user.Account.Number)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CheckUser(t, users2[0], users[0])
|
2020-08-25 13:59:19 +03:00
|
|
|
|
|
|
|
|
|
var users3 []User
|
|
|
|
|
if err := DB.Preload("Account", func(tx *gorm.DB) *gorm.DB {
|
|
|
|
|
return tx.Table("accounts AS a").Select("a.*")
|
|
|
|
|
}).Find(&users3, "id IN ?", userIDs).Error; err != nil {
|
|
|
|
|
t.Errorf("failed to query, got error %v", err)
|
|
|
|
|
}
|
|
|
|
|
sort.Slice(users3, func(i, j int) bool {
|
|
|
|
|
return users2[i].ID < users2[j].ID
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
for i, u := range users3 {
|
|
|
|
|
CheckUser(t, u, users[i])
|
|
|
|
|
}
|
2021-11-08 14:40:40 +03:00
|
|
|
|
|
|
|
|
|
var user4 User
|
|
|
|
|
DB.Delete(&users3[0].Account)
|
|
|
|
|
|
|
|
|
|
if err := DB.Preload(clause.Associations).Take(&user4, "id = ?", users3[0].ID).Error; err != nil || user4.Account.ID != 0 {
|
|
|
|
|
t.Errorf("failed to query, got error %v, account: %#v", err, user4.Account)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := DB.Preload(clause.Associations, func(tx *gorm.DB) *gorm.DB {
|
|
|
|
|
return tx.Unscoped()
|
|
|
|
|
}).Take(&user4, "id = ?", users3[0].ID).Error; err != nil || user4.Account.ID == 0 {
|
|
|
|
|
t.Errorf("failed to query, got error %v, account: %#v", err, user4.Account)
|
|
|
|
|
}
|
2020-05-23 20:16:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNestedPreloadWithConds(t *testing.T) {
|
2022-01-06 10:02:53 +03:00
|
|
|
|
users := []User{
|
2020-05-23 20:16:08 +03:00
|
|
|
|
*GetUser("slice_nested_preload_1", Config{Pets: 2}),
|
|
|
|
|
*GetUser("slice_nested_preload_2", Config{Pets: 0}),
|
|
|
|
|
*GetUser("slice_nested_preload_3", Config{Pets: 3}),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, user := range users {
|
|
|
|
|
for idx, pet := range user.Pets {
|
|
|
|
|
pet.Toy = Toy{Name: user.Name + "_toy_nested_preload_" + strconv.Itoa(idx+1)}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := DB.Create(&users).Error; err != nil {
|
|
|
|
|
t.Fatalf("errors happened when create: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var userIDs []uint
|
|
|
|
|
for _, user := range users {
|
|
|
|
|
userIDs = append(userIDs, user.ID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var users2 []User
|
|
|
|
|
DB.Preload("Pets.Toy", "name like ?", `%preload_3`).Find(&users2, "id IN ?", userIDs)
|
|
|
|
|
|
|
|
|
|
for idx, user := range users2[0:2] {
|
|
|
|
|
for _, pet := range user.Pets {
|
|
|
|
|
if pet.Toy.Name != "" {
|
|
|
|
|
t.Errorf("No toy should for user %v's pet %v but got %v", idx+1, pet.Name, pet.Toy.Name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(users2[2].Pets) != 3 {
|
|
|
|
|
t.Errorf("Invalid pet toys found for user 3 got %v", len(users2[2].Pets))
|
|
|
|
|
} else {
|
|
|
|
|
sort.Slice(users2[2].Pets, func(i, j int) bool {
|
|
|
|
|
return users2[2].Pets[i].ID < users2[2].Pets[j].ID
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
for _, pet := range users2[2].Pets[0:2] {
|
|
|
|
|
if pet.Toy.Name != "" {
|
|
|
|
|
t.Errorf("No toy should for user %v's pet %v but got %v", 3, pet.Name, pet.Toy.Name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CheckPet(t, *users2[2].Pets[2], *users[2].Pets[2])
|
|
|
|
|
}
|
2020-05-23 19:52:25 +03:00
|
|
|
|
}
|
2020-09-04 14:02:37 +03:00
|
|
|
|
|
|
|
|
|
func TestPreloadEmptyData(t *testing.T) {
|
2022-01-06 10:02:53 +03:00
|
|
|
|
user := *GetUser("user_without_associations", Config{})
|
2020-09-04 14:02:37 +03:00
|
|
|
|
DB.Create(&user)
|
|
|
|
|
|
|
|
|
|
DB.Preload("Team").Preload("Languages").Preload("Friends").First(&user, "name = ?", user.Name)
|
|
|
|
|
|
|
|
|
|
if r, err := json.Marshal(&user); err != nil {
|
|
|
|
|
t.Errorf("failed to marshal users, got error %v", err)
|
|
|
|
|
} else if !regexp.MustCompile(`"Team":\[\],"Languages":\[\],"Friends":\[\]`).MatchString(string(r)) {
|
|
|
|
|
t.Errorf("json marshal is not empty slice, got %v", string(r))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var results []User
|
|
|
|
|
DB.Preload("Team").Preload("Languages").Preload("Friends").Find(&results, "name = ?", user.Name)
|
|
|
|
|
|
|
|
|
|
if r, err := json.Marshal(&results); err != nil {
|
|
|
|
|
t.Errorf("failed to marshal users, got error %v", err)
|
|
|
|
|
} else if !regexp.MustCompile(`"Team":\[\],"Languages":\[\],"Friends":\[\]`).MatchString(string(r)) {
|
|
|
|
|
t.Errorf("json marshal is not empty slice, got %v", string(r))
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-27 09:32:20 +03:00
|
|
|
|
|
|
|
|
|
func TestPreloadGoroutine(t *testing.T) {
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
|
|
wg.Add(10)
|
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
|
go func() {
|
|
|
|
|
defer wg.Done()
|
|
|
|
|
var user2 []User
|
|
|
|
|
tx := DB.Where("id = ?", 1).Session(&gorm.Session{})
|
|
|
|
|
|
|
|
|
|
if err := tx.Preload("Team").Find(&user2).Error; err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
wg.Wait()
|
|
|
|
|
}
|
2022-03-18 08:38:46 +03:00
|
|
|
|
|
|
|
|
|
func TestPreloadWithDiffModel(t *testing.T) {
|
|
|
|
|
user := *GetUser("preload_with_diff_model", Config{Account: true})
|
|
|
|
|
|
|
|
|
|
if err := DB.Create(&user).Error; err != nil {
|
|
|
|
|
t.Fatalf("errors happened when create: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var result struct {
|
|
|
|
|
Something string
|
|
|
|
|
User
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DB.Model(User{}).Preload("Account", clause.Eq{Column: "number", Value: user.Account.Number}).Select(
|
|
|
|
|
"users.*, 'yo' as something").First(&result, "name = ?", user.Name)
|
|
|
|
|
|
|
|
|
|
CheckUser(t, user, result.User)
|
|
|
|
|
}
|
2023-02-18 04:06:43 +03:00
|
|
|
|
|
|
|
|
|
func TestNestedPreloadWithUnscoped(t *testing.T) {
|
|
|
|
|
user := *GetUser("nested_preload", Config{Pets: 1})
|
|
|
|
|
pet := user.Pets[0]
|
|
|
|
|
pet.Toy = Toy{Name: "toy_nested_preload_" + strconv.Itoa(1)}
|
|
|
|
|
pet.Toy = Toy{Name: "toy_nested_preload_" + strconv.Itoa(2)}
|
|
|
|
|
|
|
|
|
|
if err := DB.Create(&user).Error; err != nil {
|
|
|
|
|
t.Fatalf("errors happened when create: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var user2 User
|
|
|
|
|
DB.Preload("Pets.Toy").Find(&user2, "id = ?", user.ID)
|
|
|
|
|
CheckUser(t, user2, user)
|
|
|
|
|
|
|
|
|
|
DB.Delete(&pet)
|
|
|
|
|
|
|
|
|
|
var user3 User
|
|
|
|
|
DB.Preload(clause.Associations+"."+clause.Associations).Find(&user3, "id = ?", user.ID)
|
|
|
|
|
if len(user3.Pets) != 0 {
|
|
|
|
|
t.Fatalf("User.Pet[0] was deleted and should not exist.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var user4 *User
|
|
|
|
|
DB.Preload("Pets.Toy").Find(&user4, "id = ?", user.ID)
|
|
|
|
|
if len(user4.Pets) != 0 {
|
|
|
|
|
t.Fatalf("User.Pet[0] was deleted and should not exist.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var user5 User
|
|
|
|
|
DB.Unscoped().Preload(clause.Associations+"."+clause.Associations).Find(&user5, "id = ?", user.ID)
|
|
|
|
|
CheckUserUnscoped(t, user5, user)
|
|
|
|
|
|
|
|
|
|
var user6 *User
|
|
|
|
|
DB.Unscoped().Preload("Pets.Toy").Find(&user6, "id = ?", user.ID)
|
|
|
|
|
CheckUserUnscoped(t, *user6, user)
|
|
|
|
|
}
|
2023-04-11 08:10:38 +03:00
|
|
|
|
|
2024-01-29 06:34:57 +03:00
|
|
|
|
func TestNestedPreloadWithNestedJoin(t *testing.T) {
|
|
|
|
|
type (
|
|
|
|
|
Preload struct {
|
|
|
|
|
ID uint
|
|
|
|
|
Value string
|
|
|
|
|
NestedID uint
|
|
|
|
|
}
|
|
|
|
|
Join struct {
|
|
|
|
|
ID uint
|
|
|
|
|
Value string
|
|
|
|
|
NestedID uint
|
|
|
|
|
}
|
|
|
|
|
Nested struct {
|
|
|
|
|
ID uint
|
|
|
|
|
Preloads []*Preload
|
|
|
|
|
Join Join
|
|
|
|
|
ValueID uint
|
|
|
|
|
}
|
|
|
|
|
Value struct {
|
|
|
|
|
ID uint
|
|
|
|
|
Name string
|
|
|
|
|
Nested Nested
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
DB.Migrator().DropTable(&Preload{}, &Join{}, &Nested{}, &Value{})
|
|
|
|
|
DB.Migrator().AutoMigrate(&Preload{}, &Join{}, &Nested{}, &Value{})
|
|
|
|
|
|
2024-04-25 15:21:03 +03:00
|
|
|
|
value1 := Value{
|
2024-01-29 06:34:57 +03:00
|
|
|
|
Name: "value",
|
|
|
|
|
Nested: Nested{
|
|
|
|
|
Preloads: []*Preload{
|
|
|
|
|
{Value: "p1"}, {Value: "p2"},
|
|
|
|
|
},
|
|
|
|
|
Join: Join{Value: "j1"},
|
|
|
|
|
},
|
|
|
|
|
}
|
2024-04-25 15:21:03 +03:00
|
|
|
|
value2 := Value{
|
|
|
|
|
Name: "value2",
|
|
|
|
|
Nested: Nested{
|
|
|
|
|
Preloads: []*Preload{
|
|
|
|
|
{Value: "p3"}, {Value: "p4"}, {Value: "p5"},
|
|
|
|
|
},
|
|
|
|
|
Join: Join{Value: "j2"},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
values := []*Value{&value1, &value2}
|
|
|
|
|
if err := DB.Create(&values).Error; err != nil {
|
2024-01-29 06:34:57 +03:00
|
|
|
|
t.Errorf("failed to create value, got err: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var find1 Value
|
2024-04-25 15:21:03 +03:00
|
|
|
|
err := DB.Joins("Nested").Joins("Nested.Join").Preload("Nested.Preloads").First(&find1, value1.ID).Error
|
2024-01-29 06:34:57 +03:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("failed to find value, got err: %v", err)
|
|
|
|
|
}
|
2024-04-25 15:21:03 +03:00
|
|
|
|
AssertEqual(t, find1, value1)
|
2024-01-29 06:34:57 +03:00
|
|
|
|
|
|
|
|
|
var find2 Value
|
|
|
|
|
// Joins will automatically add Nested queries.
|
2024-04-25 15:21:03 +03:00
|
|
|
|
err = DB.Joins("Nested.Join").Preload("Nested.Preloads").First(&find2, value2.ID).Error
|
2024-01-29 06:34:57 +03:00
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("failed to find value, got err: %v", err)
|
|
|
|
|
}
|
2024-04-25 15:21:03 +03:00
|
|
|
|
AssertEqual(t, find2, value2)
|
2024-03-09 16:27:19 +03:00
|
|
|
|
|
|
|
|
|
var finds []Value
|
|
|
|
|
err = DB.Joins("Nested.Join").Joins("Nested").Preload("Nested.Preloads").Find(&finds).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("failed to find value, got err: %v", err)
|
|
|
|
|
}
|
2024-04-25 15:21:03 +03:00
|
|
|
|
AssertEqual(t, len(finds), 2)
|
|
|
|
|
AssertEqual(t, finds[0], value1)
|
|
|
|
|
AssertEqual(t, finds[1], value2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestMergeNestedPreloadWithNestedJoin(t *testing.T) {
|
|
|
|
|
users := []User{
|
|
|
|
|
{
|
|
|
|
|
Name: "TestMergeNestedPreloadWithNestedJoin-1",
|
|
|
|
|
Manager: &User{
|
|
|
|
|
Name: "Alexis Manager",
|
|
|
|
|
Tools: []Tools{
|
|
|
|
|
{Name: "Alexis Tool 1"},
|
|
|
|
|
{Name: "Alexis Tool 2"},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "TestMergeNestedPreloadWithNestedJoin-2",
|
|
|
|
|
Manager: &User{
|
|
|
|
|
Name: "Jinzhu Manager",
|
|
|
|
|
Tools: []Tools{
|
|
|
|
|
{Name: "Jinzhu Tool 1"},
|
|
|
|
|
{Name: "Jinzhu Tool 2"},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DB.Create(&users)
|
|
|
|
|
|
|
|
|
|
query := make([]string, 0)
|
|
|
|
|
sess := DB.Session(&gorm.Session{Logger: Tracer{
|
|
|
|
|
Logger: DB.Config.Logger,
|
|
|
|
|
Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
|
|
|
|
|
sql, _ := fc()
|
|
|
|
|
query = append(query, sql)
|
|
|
|
|
},
|
|
|
|
|
}})
|
|
|
|
|
|
|
|
|
|
var result []User
|
|
|
|
|
err := sess.
|
|
|
|
|
Joins("Manager").
|
|
|
|
|
Preload("Manager.Tools").
|
|
|
|
|
Where("users.name Like ?", "TestMergeNestedPreloadWithNestedJoin%").
|
|
|
|
|
Find(&result).Error
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to preload and find users: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AssertEqual(t, result, users)
|
|
|
|
|
AssertEqual(t, len(query), 2) // Check preload queries are merged
|
|
|
|
|
|
|
|
|
|
if !regexp.MustCompile(`SELECT \* FROM .*tools.* WHERE .*IN.*`).MatchString(query[0]) {
|
|
|
|
|
t.Fatalf("Expected first query to preload manager tools, got: %s", query[0])
|
|
|
|
|
}
|
2024-01-29 06:34:57 +03:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 13:00:47 +03:00
|
|
|
|
func TestNestedPreloadWithPointerJoin(t *testing.T) {
|
|
|
|
|
type (
|
|
|
|
|
Preload struct {
|
|
|
|
|
ID uint
|
|
|
|
|
Value string
|
|
|
|
|
JoinID uint
|
|
|
|
|
}
|
|
|
|
|
Join struct {
|
|
|
|
|
ID uint
|
|
|
|
|
Value string
|
|
|
|
|
Preload Preload
|
|
|
|
|
NestedID uint
|
|
|
|
|
}
|
|
|
|
|
Nested struct {
|
|
|
|
|
ID uint
|
|
|
|
|
Join Join
|
|
|
|
|
ValueID uint
|
|
|
|
|
}
|
|
|
|
|
Value struct {
|
|
|
|
|
ID uint
|
|
|
|
|
Name string
|
|
|
|
|
Nested *Nested
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
DB.Migrator().DropTable(&Preload{}, &Join{}, &Nested{}, &Value{})
|
|
|
|
|
DB.Migrator().AutoMigrate(&Preload{}, &Join{}, &Nested{}, &Value{})
|
|
|
|
|
|
|
|
|
|
value := Value{
|
|
|
|
|
Name: "value",
|
|
|
|
|
Nested: &Nested{
|
|
|
|
|
Join: Join{
|
|
|
|
|
Value: "j1",
|
|
|
|
|
Preload: Preload{
|
|
|
|
|
Value: "p1",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := DB.Create(&value).Error; err != nil {
|
|
|
|
|
t.Errorf("failed to create value, got err: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var find1 Value
|
|
|
|
|
err := DB.Table("values").Joins("Nested").Joins("Nested.Join").Preload("Nested.Join.Preload").First(&find1).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("failed to find value, got err: %v", err)
|
|
|
|
|
}
|
|
|
|
|
AssertEqual(t, find1, value)
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-11 08:10:38 +03:00
|
|
|
|
func TestEmbedPreload(t *testing.T) {
|
|
|
|
|
type Country struct {
|
|
|
|
|
ID int `gorm:"primaryKey"`
|
|
|
|
|
Name string
|
|
|
|
|
}
|
|
|
|
|
type EmbeddedAddress struct {
|
|
|
|
|
ID int
|
|
|
|
|
Name string
|
|
|
|
|
CountryID *int
|
|
|
|
|
Country *Country
|
|
|
|
|
}
|
|
|
|
|
type NestedAddress struct {
|
|
|
|
|
EmbeddedAddress
|
|
|
|
|
}
|
|
|
|
|
type Org struct {
|
|
|
|
|
ID int
|
|
|
|
|
PostalAddress EmbeddedAddress `gorm:"embedded;embeddedPrefix:postal_address_"`
|
|
|
|
|
VisitingAddress EmbeddedAddress `gorm:"embedded;embeddedPrefix:visiting_address_"`
|
|
|
|
|
AddressID *int
|
|
|
|
|
Address *EmbeddedAddress
|
|
|
|
|
NestedAddress NestedAddress `gorm:"embedded;embeddedPrefix:nested_address_"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DB.Migrator().DropTable(&Org{}, &EmbeddedAddress{}, &Country{})
|
|
|
|
|
DB.AutoMigrate(&Org{}, &EmbeddedAddress{}, &Country{})
|
|
|
|
|
|
|
|
|
|
org := Org{
|
|
|
|
|
PostalAddress: EmbeddedAddress{Name: "a1", Country: &Country{Name: "c1"}},
|
|
|
|
|
VisitingAddress: EmbeddedAddress{Name: "a2", Country: &Country{Name: "c2"}},
|
|
|
|
|
Address: &EmbeddedAddress{Name: "a3", Country: &Country{Name: "c3"}},
|
|
|
|
|
NestedAddress: NestedAddress{
|
|
|
|
|
EmbeddedAddress: EmbeddedAddress{Name: "a4", Country: &Country{Name: "c4"}},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
if err := DB.Create(&org).Error; err != nil {
|
|
|
|
|
t.Errorf("failed to create org, got err: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
preloads map[string][]interface{}
|
|
|
|
|
expect Org
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "address country",
|
|
|
|
|
preloads: map[string][]interface{}{"Address.Country": {}},
|
|
|
|
|
expect: Org{
|
|
|
|
|
ID: org.ID,
|
|
|
|
|
PostalAddress: EmbeddedAddress{
|
|
|
|
|
ID: org.PostalAddress.ID,
|
|
|
|
|
Name: org.PostalAddress.Name,
|
|
|
|
|
CountryID: org.PostalAddress.CountryID,
|
|
|
|
|
Country: nil,
|
|
|
|
|
},
|
|
|
|
|
VisitingAddress: EmbeddedAddress{
|
|
|
|
|
ID: org.VisitingAddress.ID,
|
|
|
|
|
Name: org.VisitingAddress.Name,
|
|
|
|
|
CountryID: org.VisitingAddress.CountryID,
|
|
|
|
|
Country: nil,
|
|
|
|
|
},
|
|
|
|
|
AddressID: org.AddressID,
|
|
|
|
|
Address: org.Address,
|
|
|
|
|
NestedAddress: NestedAddress{EmbeddedAddress{
|
|
|
|
|
ID: org.NestedAddress.ID,
|
|
|
|
|
Name: org.NestedAddress.Name,
|
|
|
|
|
CountryID: org.NestedAddress.CountryID,
|
|
|
|
|
Country: nil,
|
|
|
|
|
}},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
name: "postal address country",
|
|
|
|
|
preloads: map[string][]interface{}{"PostalAddress.Country": {}},
|
|
|
|
|
expect: Org{
|
|
|
|
|
ID: org.ID,
|
|
|
|
|
PostalAddress: org.PostalAddress,
|
|
|
|
|
VisitingAddress: EmbeddedAddress{
|
|
|
|
|
ID: org.VisitingAddress.ID,
|
|
|
|
|
Name: org.VisitingAddress.Name,
|
|
|
|
|
CountryID: org.VisitingAddress.CountryID,
|
|
|
|
|
Country: nil,
|
|
|
|
|
},
|
|
|
|
|
AddressID: org.AddressID,
|
|
|
|
|
Address: nil,
|
|
|
|
|
NestedAddress: NestedAddress{EmbeddedAddress{
|
|
|
|
|
ID: org.NestedAddress.ID,
|
|
|
|
|
Name: org.NestedAddress.Name,
|
|
|
|
|
CountryID: org.NestedAddress.CountryID,
|
|
|
|
|
Country: nil,
|
|
|
|
|
}},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
name: "nested address country",
|
2024-04-15 06:20:20 +03:00
|
|
|
|
preloads: map[string][]interface{}{"NestedAddress.Country": {}},
|
2023-04-11 08:10:38 +03:00
|
|
|
|
expect: Org{
|
|
|
|
|
ID: org.ID,
|
|
|
|
|
PostalAddress: EmbeddedAddress{
|
|
|
|
|
ID: org.PostalAddress.ID,
|
|
|
|
|
Name: org.PostalAddress.Name,
|
|
|
|
|
CountryID: org.PostalAddress.CountryID,
|
|
|
|
|
Country: nil,
|
|
|
|
|
},
|
|
|
|
|
VisitingAddress: EmbeddedAddress{
|
|
|
|
|
ID: org.VisitingAddress.ID,
|
|
|
|
|
Name: org.VisitingAddress.Name,
|
|
|
|
|
CountryID: org.VisitingAddress.CountryID,
|
|
|
|
|
Country: nil,
|
|
|
|
|
},
|
|
|
|
|
AddressID: org.AddressID,
|
|
|
|
|
Address: nil,
|
|
|
|
|
NestedAddress: org.NestedAddress,
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
name: "associations",
|
|
|
|
|
preloads: map[string][]interface{}{
|
|
|
|
|
clause.Associations: {},
|
|
|
|
|
// clause.Associations won’t preload nested associations
|
|
|
|
|
"Address.Country": {},
|
|
|
|
|
},
|
|
|
|
|
expect: org,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
|
actual := Org{}
|
|
|
|
|
tx := DB.Where("id = ?", org.ID).Session(&gorm.Session{})
|
|
|
|
|
for name, args := range test.preloads {
|
|
|
|
|
tx = tx.Preload(name, args...)
|
|
|
|
|
}
|
|
|
|
|
if err := tx.Find(&actual).Error; err != nil {
|
|
|
|
|
t.Errorf("failed to find org, got err: %v", err)
|
|
|
|
|
}
|
|
|
|
|
AssertEqual(t, actual, test.expect)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|