gorm/tests/scan_test.go

342 lines
11 KiB
Go

package tests_test
import (
"reflect"
"sort"
"strings"
"testing"
"time"
"gorm.io/gorm"
. "gorm.io/gorm/utils/tests"
)
type PersonAddressInfo struct {
Person *Person `gorm:"embedded"`
Address *Address `gorm:"embedded"`
}
func TestScan(t *testing.T) {
user1 := User{Name: "ScanUser1", Age: 1}
user2 := User{Name: "ScanUser2", Age: 10}
user3 := User{Name: "ScanUser3", Age: 20}
DB.Save(&user1).Save(&user2).Save(&user3)
type result struct {
ID uint
Name string
Age int
}
var res result
DB.Table("users").Select("id, name, age").Where("id = ?", user3.ID).Scan(&res)
if res.ID != user3.ID || res.Name != user3.Name || res.Age != int(user3.Age) {
t.Fatalf("Scan into struct should work, got %#v, should %#v", res, user3)
}
var resPointer *result
if err := DB.Table("users").Select("id, name, age").Where("id = ?", user3.ID).Scan(&resPointer).Error; err != nil {
t.Fatalf("Failed to query with pointer of value, got error %v", err)
} else if resPointer.ID != user3.ID || resPointer.Name != user3.Name || resPointer.Age != int(user3.Age) {
t.Fatalf("Scan into struct should work, got %#v, should %#v", res, user3)
}
DB.Table("users").Select("id, name, age").Where("id = ?", user2.ID).Scan(&res)
if res.ID != user2.ID || res.Name != user2.Name || res.Age != int(user2.Age) {
t.Fatalf("Scan into struct should work, got %#v, should %#v", res, user2)
}
DB.Model(&User{Model: gorm.Model{ID: user3.ID}}).Select("id, name, age").Scan(&res)
if res.ID != user3.ID || res.Name != user3.Name || res.Age != int(user3.Age) {
t.Fatalf("Scan into struct should work, got %#v, should %#v", res, user3)
}
doubleAgeRes := &result{}
if err := DB.Table("users").Select("age + age as age").Where("id = ?", user3.ID).Scan(&doubleAgeRes).Error; err != nil {
t.Errorf("Scan to pointer of pointer")
}
if doubleAgeRes.Age != int(res.Age)*2 {
t.Errorf("Scan double age as age, expect: %v, got %v", res.Age*2, doubleAgeRes.Age)
}
var results []result
DB.Table("users").Select("name, age").Where("id in ?", []uint{user2.ID, user3.ID}).Scan(&results)
sort.Slice(results, func(i, j int) bool {
return strings.Compare(results[i].Name, results[j].Name) <= -1
})
if len(results) != 2 || results[0].Name != user2.Name || results[1].Name != user3.Name {
t.Errorf("Scan into struct map, got %#v", results)
}
type ID uint64
var id ID
DB.Raw("select id from users where id = ?", user2.ID).Scan(&id)
if uint(id) != user2.ID {
t.Errorf("Failed to scan to customized data type")
}
var resInt interface{}
resInt = &User{}
if err := DB.Table("users").Select("id, name, age").Where("id = ?", user3.ID).Find(&resInt).Error; err != nil {
t.Fatalf("Failed to query with pointer of value, got error %v", err)
} else if resInt.(*User).ID != user3.ID || resInt.(*User).Name != user3.Name || resInt.(*User).Age != user3.Age {
t.Fatalf("Scan into struct should work, got %#v, should %#v", resInt, user3)
}
var resInt2 interface{}
resInt2 = &User{}
if err := DB.Table("users").Select("id, name, age").Where("id = ?", user3.ID).Scan(&resInt2).Error; err != nil {
t.Fatalf("Failed to query with pointer of value, got error %v", err)
} else if resInt2.(*User).ID != user3.ID || resInt2.(*User).Name != user3.Name || resInt2.(*User).Age != user3.Age {
t.Fatalf("Scan into struct should work, got %#v, should %#v", resInt2, user3)
}
var resInt3 interface{}
resInt3 = []User{}
if err := DB.Table("users").Select("id, name, age").Where("id = ?", user3.ID).Find(&resInt3).Error; err != nil {
t.Fatalf("Failed to query with pointer of value, got error %v", err)
} else if rus := resInt3.([]User); len(rus) == 0 || rus[0].ID != user3.ID || rus[0].Name != user3.Name || rus[0].Age != user3.Age {
t.Fatalf("Scan into struct should work, got %#v, should %#v", resInt3, user3)
}
var resInt4 interface{}
resInt4 = []User{}
if err := DB.Table("users").Select("id, name, age").Where("id = ?", user3.ID).Scan(&resInt4).Error; err != nil {
t.Fatalf("Failed to query with pointer of value, got error %v", err)
} else if rus := resInt4.([]User); len(rus) == 0 || rus[0].ID != user3.ID || rus[0].Name != user3.Name || rus[0].Age != user3.Age {
t.Fatalf("Scan into struct should work, got %#v, should %#v", resInt4, user3)
}
var resInt5 interface{}
resInt5 = []User{}
if err := DB.Table("users").Select("id, name, age").Where("id IN ?", []uint{user1.ID, user2.ID, user3.ID}).Find(&resInt5).Error; err != nil {
t.Fatalf("Failed to query with pointer of value, got error %v", err)
} else if rus := resInt5.([]User); len(rus) != 3 {
t.Fatalf("Scan into struct should work, got %+v, len %v", resInt5, len(rus))
}
}
func TestScanRows(t *testing.T) {
user1 := User{Name: "ScanRowsUser1", Age: 1}
user2 := User{Name: "ScanRowsUser2", Age: 10}
user3 := User{Name: "ScanRowsUser3", Age: 20}
DB.Save(&user1).Save(&user2).Save(&user3)
rows, err := DB.Table("users").Where("name = ? or name = ?", user2.Name, user3.Name).Select("name, age").Rows()
if err != nil {
t.Errorf("No error should happen, got %v", err)
}
type Result struct {
Name string
Age int
}
var results []Result
for rows.Next() {
var result Result
if err := DB.ScanRows(rows, &result); err != nil {
t.Errorf("should get no error, but got %v", err)
}
results = append(results, result)
}
sort.Slice(results, func(i, j int) bool {
return strings.Compare(results[i].Name, results[j].Name) <= -1
})
if !reflect.DeepEqual(results, []Result{{Name: "ScanRowsUser2", Age: 10}, {Name: "ScanRowsUser3", Age: 20}}) {
t.Errorf("Should find expected results, got %+v", results)
}
var ages int
if err := DB.Table("users").Where("name = ? or name = ?", user2.Name, user3.Name).Select("SUM(age)").Scan(&ages).Error; err != nil || ages != 30 {
t.Fatalf("failed to scan ages, got error %v, ages: %v", err, ages)
}
var name string
if err := DB.Table("users").Where("name = ?", user2.Name).Select("name").Scan(&name).Error; err != nil || name != user2.Name {
t.Fatalf("failed to scan name, got error %v, name: %v", err, name)
}
}
func TestScanRowsNullValuesScanToFieldDefault(t *testing.T) {
DB.Save(&User{})
rows, err := DB.Table("users").
Select(`
NULL AS bool_field,
NULL AS int_field,
NULL AS int8_field,
NULL AS int16_field,
NULL AS int32_field,
NULL AS int64_field,
NULL AS uint_field,
NULL AS uint8_field,
NULL AS uint16_field,
NULL AS uint32_field,
NULL AS uint64_field,
NULL AS float32_field,
NULL AS float64_field,
NULL AS string_field,
NULL AS time_field,
NULL AS time_ptr_field,
NULL AS embedded_int_field,
NULL AS nested_embedded_int_field,
NULL AS embedded_ptr_int_field
`).Rows()
if err != nil {
t.Errorf("No error should happen, got %v", err)
}
type NestedEmbeddedStruct struct {
NestedEmbeddedIntField int
NestedEmbeddedIntFieldWithDefault int `gorm:"default:2"`
}
type EmbeddedStruct struct {
EmbeddedIntField int
NestedEmbeddedStruct `gorm:"embedded"`
}
type EmbeddedPtrStruct struct {
EmbeddedPtrIntField int
*NestedEmbeddedStruct `gorm:"embedded"`
}
type Result struct {
BoolField bool
IntField int
Int8Field int8
Int16Field int16
Int32Field int32
Int64Field int64
UIntField uint
UInt8Field uint8
UInt16Field uint16
UInt32Field uint32
UInt64Field uint64
Float32Field float32
Float64Field float64
StringField string
TimeField time.Time
TimePtrField *time.Time
EmbeddedStruct `gorm:"embedded"`
*EmbeddedPtrStruct `gorm:"embedded"`
}
currTime := time.Now()
reusedVar := Result{
BoolField: true,
IntField: 1,
Int8Field: 1,
Int16Field: 1,
Int32Field: 1,
Int64Field: 1,
UIntField: 1,
UInt8Field: 1,
UInt16Field: 1,
UInt32Field: 1,
UInt64Field: 1,
Float32Field: 1.1,
Float64Field: 1.1,
StringField: "hello",
TimeField: currTime,
TimePtrField: &currTime,
EmbeddedStruct: EmbeddedStruct{EmbeddedIntField: 1, NestedEmbeddedStruct: NestedEmbeddedStruct{NestedEmbeddedIntField: 1, NestedEmbeddedIntFieldWithDefault: 2}},
EmbeddedPtrStruct: &EmbeddedPtrStruct{EmbeddedPtrIntField: 1, NestedEmbeddedStruct: &NestedEmbeddedStruct{NestedEmbeddedIntField: 1, NestedEmbeddedIntFieldWithDefault: 2}},
}
for rows.Next() {
if err := DB.ScanRows(rows, &reusedVar); err != nil {
t.Errorf("should get no error, but got %v", err)
}
}
if !reflect.DeepEqual(reusedVar, Result{}) {
t.Errorf("Should find zero values in struct fields, got %+v\n", reusedVar)
}
}
func TestScanToEmbedded(t *testing.T) {
person1 := Person{Name: "person 1"}
person2 := Person{Name: "person 2"}
DB.Save(&person1).Save(&person2)
address1 := Address{Name: "address 1"}
address2 := Address{Name: "address 2"}
DB.Save(&address1).Save(&address2)
DB.Create(&PersonAddress{PersonID: person1.ID, AddressID: int(address1.ID)})
DB.Create(&PersonAddress{PersonID: person1.ID, AddressID: int(address2.ID)})
DB.Create(&PersonAddress{PersonID: person2.ID, AddressID: int(address1.ID)})
var personAddressInfoList []*PersonAddressInfo
if err := DB.Select("people.*, addresses.*").
Table("people").
Joins("inner join person_addresses on people.id = person_addresses.person_id").
Joins("inner join addresses on person_addresses.address_id = addresses.id").
Find(&personAddressInfoList).Error; err != nil {
t.Errorf("Failed to run join query, got error: %v", err)
}
personMatched := false
addressMatched := false
for _, info := range personAddressInfoList {
if info.Person == nil {
t.Fatalf("Failed, expected not nil, got person nil")
}
if info.Address == nil {
t.Fatalf("Failed, expected not nil, got address nil")
}
if info.Person.ID == person1.ID {
personMatched = true
if info.Person.Name != person1.Name {
t.Errorf("Failed, expected %v, got %v", person1.Name, info.Person.Name)
}
}
if info.Address.ID == address1.ID {
addressMatched = true
if info.Address.Name != address1.Name {
t.Errorf("Failed, expected %v, got %v", address1.Name, info.Address.Name)
}
}
}
if !personMatched {
t.Errorf("Failed, no person matched")
}
if !addressMatched {
t.Errorf("Failed, no address matched")
}
personDupField := Person{ID: person1.ID}
if err := DB.Select("people.id, people.*").
First(&personDupField).Error; err != nil {
t.Errorf("Failed to run join query, got error: %v", err)
}
AssertEqual(t, person1, personDupField)
user := User{
Name: "TestScanToEmbedded_1",
Manager: &User{
Name: "TestScanToEmbedded_1_m1",
Manager: &User{Name: "TestScanToEmbedded_1_m1_m1"},
},
}
DB.Create(&user)
type UserScan struct {
ID uint
Name string
ManagerID *uint
}
var user2 UserScan
err := DB.Raw("SELECT * FROM users INNER JOIN users Manager ON users.manager_id = Manager.id WHERE users.id = ?", user.ID).Scan(&user2).Error
AssertEqual(t, err, nil)
}