2020-05-23 18:50:48 +03:00
package tests_test
import (
2020-08-23 05:40:37 +03:00
"regexp"
2020-05-23 18:50:48 +03:00
"sort"
"testing"
2020-06-02 04:16:07 +03:00
"gorm.io/gorm"
2020-06-02 05:34:50 +03:00
. "gorm.io/gorm/utils/tests"
2020-05-23 18:50:48 +03:00
)
func TestJoins ( t * testing . T ) {
2022-04-13 10:47:04 +03:00
user := * GetUser ( "joins-1" , Config { Company : true , Manager : true , Account : true , NamedPet : false } )
2020-05-23 18:50:48 +03:00
DB . Create ( & user )
var user2 User
2022-04-13 10:47:04 +03:00
if err := DB . Joins ( "NamedPet" ) . Joins ( "Company" ) . Joins ( "Manager" ) . Joins ( "Account" ) . First ( & user2 , "users.name = ?" , user . Name ) . Error ; err != nil {
2020-05-23 18:50:48 +03:00
t . Fatalf ( "Failed to load with joins, got error: %v" , err )
}
CheckUser ( t , user2 , user )
}
func TestJoinsForSlice ( t * testing . T ) {
users := [ ] User {
* GetUser ( "slice-joins-1" , Config { Company : true , Manager : true , Account : true } ) ,
* GetUser ( "slice-joins-2" , Config { Company : true , Manager : true , Account : true } ) ,
* GetUser ( "slice-joins-3" , Config { Company : true , Manager : true , Account : true } ) ,
}
DB . Create ( & users )
var userIDs [ ] uint
for _ , user := range users {
userIDs = append ( userIDs , user . ID )
}
var users2 [ ] User
if err := DB . Joins ( "Company" ) . Joins ( "Manager" ) . Joins ( "Account" ) . Find ( & users2 , "users.id IN ?" , userIDs ) . Error ; err != nil {
t . Fatalf ( "Failed to load with joins, got error: %v" , err )
} else if len ( users2 ) != len ( users ) {
t . Fatalf ( "Failed to load join users, got: %v, expect: %v" , len ( users2 ) , len ( users ) )
}
sort . Slice ( users2 , func ( i , j int ) bool {
return users2 [ i ] . ID > users2 [ j ] . ID
} )
sort . Slice ( users , func ( i , j int ) bool {
return users [ i ] . ID > users [ j ] . ID
} )
for idx , user := range users {
CheckUser ( t , user , users2 [ idx ] )
}
}
2020-05-31 15:21:52 +03:00
func TestJoinConds ( t * testing . T ) {
2022-01-06 10:02:53 +03:00
user := * GetUser ( "joins-conds" , Config { Account : true , Pets : 3 } )
2020-05-31 15:21:52 +03:00
DB . Save ( & user )
var users1 [ ] User
2020-12-07 05:31:06 +03:00
DB . Joins ( "inner join pets on pets.user_id = users.id" ) . Where ( "users.name = ?" , user . Name ) . Find ( & users1 )
2020-05-31 15:21:52 +03:00
if len ( users1 ) != 3 {
t . Errorf ( "should find two users using left join, but got %v" , len ( users1 ) )
}
var users2 [ ] User
2020-12-07 05:31:06 +03:00
DB . Joins ( "inner join pets on pets.user_id = users.id AND pets.name = ?" , user . Pets [ 0 ] . Name ) . Where ( "users.name = ?" , user . Name ) . First ( & users2 )
2020-05-31 15:21:52 +03:00
if len ( users2 ) != 1 {
t . Errorf ( "should find one users using left join with conditions, but got %v" , len ( users2 ) )
}
var users3 [ ] User
2020-12-07 05:31:06 +03:00
DB . Joins ( "inner join pets on pets.user_id = users.id AND pets.name = ?" , user . Pets [ 0 ] . Name ) . Joins ( "join accounts on accounts.user_id = users.id AND accounts.number = ?" , user . Account . Number ) . Where ( "users.name = ?" , user . Name ) . First ( & users3 )
2020-05-31 15:21:52 +03:00
if len ( users3 ) != 1 {
t . Errorf ( "should find one users using multiple left join conditions, but got %v" , len ( users3 ) )
}
var users4 [ ] User
2020-12-07 05:31:06 +03:00
DB . Joins ( "inner join pets on pets.user_id = users.id AND pets.name = ?" , user . Pets [ 0 ] . Name ) . Joins ( "join accounts on accounts.user_id = users.id AND accounts.number = ?" , user . Account . Number + "non-exist" ) . Where ( "users.name = ?" , user . Name ) . First ( & users4 )
2020-05-31 15:21:52 +03:00
if len ( users4 ) != 0 {
t . Errorf ( "should find no user when searching with unexisting credit card, but got %v" , len ( users4 ) )
}
var users5 [ ] User
2020-12-07 05:31:06 +03:00
db5 := DB . Joins ( "inner join pets on pets.user_id = users.id AND pets.name = ?" , user . Pets [ 0 ] . Name ) . Joins ( "join accounts on accounts.user_id = users.id AND accounts.number = ?" , user . Account . Number ) . Where ( User { Model : gorm . Model { ID : 1 } } ) . Where ( Account { Model : gorm . Model { ID : 1 } } ) . Not ( Pet { Model : gorm . Model { ID : 1 } } ) . Find ( & users5 )
2020-05-31 15:21:52 +03:00
if db5 . Error != nil {
t . Errorf ( "Should not raise error for join where identical fields in different tables. Error: %s" , db5 . Error . Error ( ) )
}
2020-08-23 05:40:37 +03:00
2020-12-07 05:31:06 +03:00
var users6 [ ] User
DB . Joins ( "inner join pets on pets.user_id = users.id AND pets.name = @Name" , user . Pets [ 0 ] ) . Where ( "users.name = ?" , user . Name ) . First ( & users6 )
if len ( users6 ) != 1 {
t . Errorf ( "should find one users using left join with conditions, but got %v" , len ( users6 ) )
}
2020-08-23 05:40:37 +03:00
dryDB := DB . Session ( & gorm . Session { DryRun : true } )
stmt := dryDB . Joins ( "left join pets on pets.user_id = users.id AND pets.name = ?" , user . Pets [ 0 ] . Name ) . Joins ( "join accounts on accounts.user_id = users.id AND accounts.number = ?" , user . Account . Number ) . Where ( User { Model : gorm . Model { ID : 1 } } ) . Where ( Account { Model : gorm . Model { ID : 1 } } ) . Not ( Pet { Model : gorm . Model { ID : 1 } } ) . Find ( & users5 ) . Statement
if ! regexp . MustCompile ( "SELECT .* FROM .users. left join pets.*join accounts.*" ) . MatchString ( stmt . SQL . String ( ) ) {
t . Errorf ( "joins should be ordered, but got %v" , stmt . SQL . String ( ) )
}
2021-09-28 16:43:12 +03:00
iv := DB . Table ( ` table_invoices ` ) . Select ( ` seller, SUM(total) as total, SUM(paid) as paid, SUM(balance) as balance ` ) . Group ( ` seller ` )
stmt = dryDB . Table ( ` table_employees ` ) . Select ( ` id, name, iv.total, iv.paid, iv.balance ` ) . Joins ( ` LEFT JOIN (?) AS iv ON iv.seller = table_employees.id ` , iv ) . Scan ( & user ) . Statement
if ! regexp . MustCompile ( "SELECT id, name, iv.total, iv.paid, iv.balance FROM .table_employees. LEFT JOIN \\(SELECT seller, SUM\\(total\\) as total, SUM\\(paid\\) as paid, SUM\\(balance\\) as balance FROM .table_invoices. GROUP BY .seller.\\) AS iv ON iv.seller = table_employees.id" ) . MatchString ( stmt . SQL . String ( ) ) {
t . Errorf ( "joins should be ordered, but got %v" , stmt . SQL . String ( ) )
}
2020-05-31 15:21:52 +03:00
}
2021-07-17 16:45:15 +03:00
func TestJoinOn ( t * testing . T ) {
2022-01-06 10:02:53 +03:00
user := * GetUser ( "joins-on" , Config { Pets : 2 } )
2021-07-17 16:45:15 +03:00
DB . Save ( & user )
var user1 User
2021-09-07 16:21:44 +03:00
onQuery := DB . Where ( & Pet { Name : "joins-on_pet_1" } )
2021-07-17 16:45:15 +03:00
2021-08-28 11:27:19 +03:00
if err := DB . Joins ( "NamedPet" , onQuery ) . Where ( "users.name = ?" , user . Name ) . First ( & user1 ) . Error ; err != nil {
2021-07-17 16:45:15 +03:00
t . Fatalf ( "Failed to load with joins on, got error: %v" , err )
}
2021-09-07 16:21:44 +03:00
2021-07-17 16:45:15 +03:00
AssertEqual ( t , user1 . NamedPet . Name , "joins-on_pet_1" )
2021-09-07 16:21:44 +03:00
onQuery2 := DB . Where ( & Pet { Name : "joins-on_pet_2" } )
2021-07-17 16:45:15 +03:00
var user2 User
2021-08-28 11:27:19 +03:00
if err := DB . Joins ( "NamedPet" , onQuery2 ) . Where ( "users.name = ?" , user . Name ) . First ( & user2 ) . Error ; err != nil {
2021-07-17 16:45:15 +03:00
t . Fatalf ( "Failed to load with joins on, got error: %v" , err )
}
AssertEqual ( t , user2 . NamedPet . Name , "joins-on_pet_2" )
}
2020-05-31 15:21:52 +03:00
func TestJoinsWithSelect ( t * testing . T ) {
type result struct {
2020-05-31 15:42:07 +03:00
ID uint
PetID uint
Name string
2020-05-31 15:21:52 +03:00
}
user := * GetUser ( "joins_with_select" , Config { Pets : 2 } )
DB . Save ( & user )
var results [ ] result
2020-07-05 17:12:52 +03:00
2020-05-31 15:42:07 +03:00
DB . Table ( "users" ) . Select ( "users.id, pets.id as pet_id, pets.name" ) . Joins ( "left join pets on pets.user_id = users.id" ) . Where ( "users.name = ?" , "joins_with_select" ) . Scan ( & results )
sort . Slice ( results , func ( i , j int ) bool {
return results [ i ] . PetID > results [ j ] . PetID
} )
sort . Slice ( results , func ( i , j int ) bool {
return user . Pets [ i ] . ID > user . Pets [ j ] . ID
} )
2020-05-31 15:21:52 +03:00
if len ( results ) != 2 || results [ 0 ] . Name != user . Pets [ 0 ] . Name || results [ 1 ] . Name != user . Pets [ 1 ] . Name {
2020-05-31 15:42:07 +03:00
t . Errorf ( "Should find all two pets with Join select, got %+v" , results )
2020-05-31 15:21:52 +03:00
}
}
2021-10-09 11:55:45 +03:00
2022-01-28 17:16:42 +03:00
func TestJoinWithOmit ( t * testing . T ) {
user := * GetUser ( "joins_with_omit" , Config { Pets : 2 } )
DB . Save ( & user )
results := make ( [ ] * User , 0 )
if err := DB . Table ( "users" ) . Omit ( "name" ) . Where ( "users.name = ?" , "joins_with_omit" ) . Joins ( "left join pets on pets.user_id = users.id" ) . Find ( & results ) . Error ; err != nil {
return
}
if len ( results ) != 2 || results [ 0 ] . Name != "" || results [ 1 ] . Name != "" {
t . Errorf ( "Should find all two pets with Join omit and should not find user's name, got %+v" , results )
return
}
}
2021-10-09 11:55:45 +03:00
func TestJoinCount ( t * testing . T ) {
companyA := Company { Name : "A" }
companyB := Company { Name : "B" }
DB . Create ( & companyA )
DB . Create ( & companyB )
user := User { Name : "kingGo" , CompanyID : & companyB . ID }
DB . Create ( & user )
query := DB . Model ( & User { } ) . Joins ( "Company" )
2022-01-06 10:02:53 +03:00
// Bug happens when .Count is called on a query.
// Removing the below two lines or downgrading to gorm v1.20.12 will make this test pass.
2021-10-09 11:55:45 +03:00
var total int64
query . Count ( & total )
var result User
// Incorrectly generates a 'SELECT *' query which causes companies.id to overwrite users.id
if err := query . First ( & result , user . ID ) . Error ; err != nil {
t . Fatalf ( "Failed, got error: %v" , err )
}
if result . ID != user . ID {
t . Fatalf ( "result's id, %d, doesn't match user's id, %d" , result . ID , user . ID )
}
}
2022-03-17 20:07:49 +03:00
func TestJoinWithSoftDeleted ( t * testing . T ) {
user := GetUser ( "TestJoinWithSoftDeletedUser" , Config { Account : true , NamedPet : true } )
DB . Create ( & user )
var user1 User
DB . Model ( & User { } ) . Joins ( "NamedPet" ) . Joins ( "Account" ) . First ( & user1 , user . ID )
if user1 . NamedPet == nil || user1 . Account . ID == 0 {
t . Fatalf ( "joins NamedPet and Account should not empty:%v" , user1 )
}
// Account should empty
DB . Delete ( & user1 . Account )
var user2 User
DB . Model ( & User { } ) . Joins ( "NamedPet" ) . Joins ( "Account" ) . First ( & user2 , user . ID )
if user2 . NamedPet == nil || user2 . Account . ID != 0 {
t . Fatalf ( "joins Account should not empty:%v" , user2 )
}
// NamedPet should empty
DB . Delete ( & user1 . NamedPet )
var user3 User
DB . Model ( & User { } ) . Joins ( "NamedPet" ) . Joins ( "Account" ) . First ( & user3 , user . ID )
if user3 . NamedPet != nil || user2 . Account . ID != 0 {
t . Fatalf ( "joins NamedPet and Account should not empty:%v" , user2 )
}
}