Merge branch 'RichardKnop-feature/fix-preload-duplicates'

This commit is contained in:
Jinzhu 2016-07-10 21:34:49 +08:00
commit 613c065569
6 changed files with 115 additions and 2 deletions

View File

@ -57,6 +57,21 @@ func TestCreate(t *testing.T) {
} }
} }
func TestCreateWithAutoIncrement(t *testing.T) {
if dialect := os.Getenv("GORM_DIALECT"); dialect != "postgres" {
t.Skip("Skipping this because only postgres properly support auto_increment on a non-primary_key column")
}
user1 := User{}
user2 := User{}
DB.Create(&user1)
DB.Create(&user2)
if user2.Sequence-user1.Sequence != 1 {
t.Errorf("Auto increment should apply on Sequence")
}
}
func TestCreateWithNoGORMPrimayKey(t *testing.T) { func TestCreateWithNoGORMPrimayKey(t *testing.T) {
if dialect := os.Getenv("GORM_DIALECT"); dialect == "mssql" { if dialect := os.Getenv("GORM_DIALECT"); dialect == "mssql" {
t.Skip("Skipping this because MSSQL will return identity only if the table has an Id column") t.Skip("Skipping this because MSSQL will return identity only if the table has an Id column")

View File

@ -30,6 +30,14 @@ func (mysql) Quote(key string) string {
func (mysql) DataTypeOf(field *StructField) string { func (mysql) DataTypeOf(field *StructField) string {
var dataValue, sqlType, size, additionalType = ParseFieldStructForDialect(field) var dataValue, sqlType, size, additionalType = ParseFieldStructForDialect(field)
// MySQL allows only one auto increment column per table, and it must
// be a KEY column.
if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
if _, ok = field.TagSettings["INDEX"]; !ok && !field.IsPrimaryKey {
delete(field.TagSettings, "AUTO_INCREMENT")
}
}
if sqlType == "" { if sqlType == "" {
switch dataValue.Kind() { switch dataValue.Kind() {
case reflect.Bool: case reflect.Bool:

View File

@ -33,6 +33,7 @@ type User struct {
Company Company Company Company
Role Role
PasswordHash []byte PasswordHash []byte
Sequence uint `gorm:"AUTO_INCREMENT"`
IgnoreMe int64 `sql:"-"` IgnoreMe int64 `sql:"-"`
IgnoreStringSlice []string `sql:"-"` IgnoreStringSlice []string `sql:"-"`
Ignored struct{ Name string } `sql:"-"` Ignored struct{ Name string } `sql:"-"`

View File

@ -175,6 +175,10 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
field.HasDefaultValue = true field.HasDefaultValue = true
} }
if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
field.HasDefaultValue = true
}
indirectType := fieldStruct.Type indirectType := fieldStruct.Type
for indirectType.Kind() == reflect.Ptr { for indirectType.Kind() == reflect.Ptr {
indirectType = indirectType.Elem() indirectType = indirectType.Elem()

View File

@ -1509,6 +1509,88 @@ func TestNilPointerSlice2(t *testing.T) {
} }
} }
func TestPrefixedPreloadDuplication(t *testing.T) {
type (
Level4 struct {
ID uint
Name string
Level3ID uint
}
Level3 struct {
ID uint
Name string
Level4s []*Level4
}
Level2 struct {
ID uint
Name string
Level3ID sql.NullInt64 `sql:"index"`
Level3 *Level3
}
Level1 struct {
ID uint
Name string
Level2ID sql.NullInt64 `sql:"index"`
Level2 *Level2
}
)
DB.DropTableIfExists(new(Level3))
DB.DropTableIfExists(new(Level4))
DB.DropTableIfExists(new(Level2))
DB.DropTableIfExists(new(Level1))
if err := DB.AutoMigrate(new(Level3), new(Level4), new(Level2), new(Level1)).Error; err != nil {
t.Error(err)
}
lvl := &Level3{}
if err := DB.Save(lvl).Error; err != nil {
t.Error(err)
}
sublvl1 := &Level4{Level3ID: lvl.ID}
if err := DB.Save(sublvl1).Error; err != nil {
t.Error(err)
}
sublvl2 := &Level4{Level3ID: lvl.ID}
if err := DB.Save(sublvl2).Error; err != nil {
t.Error(err)
}
lvl.Level4s = []*Level4{sublvl1, sublvl2}
want1 := Level1{
Level2: &Level2{
Level3: lvl,
},
}
if err := DB.Save(&want1).Error; err != nil {
t.Error(err)
}
want2 := Level1{
Level2: &Level2{
Level3: lvl,
},
}
if err := DB.Save(&want2).Error; err != nil {
t.Error(err)
}
want := []Level1{want1, want2}
var got []Level1
err := DB.Preload("Level2.Level3.Level4s").Find(&got).Error
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want))
}
}
func toJSONString(v interface{}) []byte { func toJSONString(v interface{}) []byte {
r, _ := json.MarshalIndent(v, "", " ") r, _ := json.MarshalIndent(v, "", " ")
return r return r

View File

@ -1237,6 +1237,7 @@ func (scope *Scope) getColumnAsScope(column string) *Scope {
fieldType = fieldType.Elem() fieldType = fieldType.Elem()
} }
resultsMap := map[interface{}]bool{}
results := reflect.New(reflect.SliceOf(reflect.PtrTo(fieldType))).Elem() results := reflect.New(reflect.SliceOf(reflect.PtrTo(fieldType))).Elem()
for i := 0; i < indirectScopeValue.Len(); i++ { for i := 0; i < indirectScopeValue.Len(); i++ {
@ -1244,11 +1245,13 @@ func (scope *Scope) getColumnAsScope(column string) *Scope {
if result.Kind() == reflect.Slice { if result.Kind() == reflect.Slice {
for j := 0; j < result.Len(); j++ { for j := 0; j < result.Len(); j++ {
if elem := result.Index(j); elem.CanAddr() { if elem := result.Index(j); elem.CanAddr() && resultsMap[elem.Addr()] != true {
resultsMap[elem.Addr()] = true
results = reflect.Append(results, elem.Addr()) results = reflect.Append(results, elem.Addr())
} }
} }
} else if result.CanAddr() { } else if result.CanAddr() && resultsMap[result.Addr()] != true {
resultsMap[result.Addr()] = true
results = reflect.Append(results, result.Addr()) results = reflect.Append(results, result.Addr())
} }
} }