Merge branch 'master' into refactor-some-if
This commit is contained in:
		
						commit
						fc3075afe8
					
				@ -147,6 +147,21 @@ func BuildQuerySQL(db *gorm.DB) {
 | 
				
			|||||||
						}
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if join.On != nil {
 | 
				
			||||||
 | 
											onStmt := gorm.Statement{Table: tableAliasName, DB: db}
 | 
				
			||||||
 | 
											join.On.Build(&onStmt)
 | 
				
			||||||
 | 
											onSQL := onStmt.SQL.String()
 | 
				
			||||||
 | 
											vars := onStmt.Vars
 | 
				
			||||||
 | 
											for idx, v := range onStmt.Vars {
 | 
				
			||||||
 | 
												bindvar := strings.Builder{}
 | 
				
			||||||
 | 
												onStmt.Vars = vars[0 : idx+1]
 | 
				
			||||||
 | 
												db.Dialector.BindVarTo(&bindvar, &onStmt, v)
 | 
				
			||||||
 | 
												onSQL = strings.Replace(onSQL, bindvar.String(), "?", 1)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											exprs = append(exprs, clause.Expr{SQL: onSQL, Vars: vars})
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					joins = append(joins, clause.Join{
 | 
										joins = append(joins, clause.Join{
 | 
				
			||||||
						Type:  clause.LeftJoin,
 | 
											Type:  clause.LeftJoin,
 | 
				
			||||||
						Table: clause.Table{Name: relation.FieldSchema.Table, Alias: tableAliasName},
 | 
											Table: clause.Table{Name: relation.FieldSchema.Table, Alias: tableAliasName},
 | 
				
			||||||
 | 
				
			|||||||
@ -171,8 +171,19 @@ func (db *DB) Or(query interface{}, args ...interface{}) (tx *DB) {
 | 
				
			|||||||
// Joins specify Joins conditions
 | 
					// Joins specify Joins conditions
 | 
				
			||||||
//     db.Joins("Account").Find(&user)
 | 
					//     db.Joins("Account").Find(&user)
 | 
				
			||||||
//     db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Find(&user)
 | 
					//     db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Find(&user)
 | 
				
			||||||
 | 
					//     db.Joins("Account", DB.Select("id").Where("user_id = users.id AND name = ?", "someName").Model(&Account{}))
 | 
				
			||||||
func (db *DB) Joins(query string, args ...interface{}) (tx *DB) {
 | 
					func (db *DB) Joins(query string, args ...interface{}) (tx *DB) {
 | 
				
			||||||
	tx = db.getInstance()
 | 
						tx = db.getInstance()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(args) > 0 {
 | 
				
			||||||
 | 
							if db, ok := args[0].(*DB); ok {
 | 
				
			||||||
 | 
								if where, ok := db.Statement.Clauses["WHERE"].Expression.(clause.Where); ok {
 | 
				
			||||||
 | 
									tx.Statement.Joins = append(tx.Statement.Joins, join{Name: query, Conds: args[1:], On: &where})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tx.Statement.Joins = append(tx.Statement.Joins, join{Name: query, Conds: args})
 | 
						tx.Statement.Joins = append(tx.Statement.Joins, join{Name: query, Conds: args})
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -119,20 +119,13 @@ func Parse(dest interface{}, cacheStore *sync.Map, namer Namer) (*Schema, error)
 | 
				
			|||||||
	// When the schema initialization is completed, the channel will be closed
 | 
						// When the schema initialization is completed, the channel will be closed
 | 
				
			||||||
	defer close(schema.initialized)
 | 
						defer close(schema.initialized)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if v, loaded := cacheStore.LoadOrStore(modelType, schema); loaded {
 | 
						if v, loaded := cacheStore.Load(modelType); loaded {
 | 
				
			||||||
		s := v.(*Schema)
 | 
							s := v.(*Schema)
 | 
				
			||||||
		// Wait for the initialization of other goroutines to complete
 | 
							// Wait for the initialization of other goroutines to complete
 | 
				
			||||||
		<-s.initialized
 | 
							<-s.initialized
 | 
				
			||||||
		return s, s.err
 | 
							return s, s.err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defer func() {
 | 
					 | 
				
			||||||
		if schema.err != nil {
 | 
					 | 
				
			||||||
			logger.Default.Error(context.Background(), schema.err.Error())
 | 
					 | 
				
			||||||
			cacheStore.Delete(modelType)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i := 0; i < modelType.NumField(); i++ {
 | 
						for i := 0; i < modelType.NumField(); i++ {
 | 
				
			||||||
		if fieldStruct := modelType.Field(i); ast.IsExported(fieldStruct.Name) {
 | 
							if fieldStruct := modelType.Field(i); ast.IsExported(fieldStruct.Name) {
 | 
				
			||||||
			if field := schema.ParseField(fieldStruct); field.EmbeddedSchema != nil {
 | 
								if field := schema.ParseField(fieldStruct); field.EmbeddedSchema != nil {
 | 
				
			||||||
@ -233,6 +226,20 @@ func Parse(dest interface{}, cacheStore *sync.Map, namer Namer) (*Schema, error)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if v, loaded := cacheStore.LoadOrStore(modelType, schema); loaded {
 | 
				
			||||||
 | 
							s := v.(*Schema)
 | 
				
			||||||
 | 
							// Wait for the initialization of other goroutines to complete
 | 
				
			||||||
 | 
							<-s.initialized
 | 
				
			||||||
 | 
							return s, s.err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if schema.err != nil {
 | 
				
			||||||
 | 
								logger.Default.Error(context.Background(), schema.err.Error())
 | 
				
			||||||
 | 
								cacheStore.Delete(modelType)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, embedded := schema.cacheStore.Load(embeddedCacheKey); !embedded {
 | 
						if _, embedded := schema.cacheStore.Load(embeddedCacheKey); !embedded {
 | 
				
			||||||
		for _, field := range schema.Fields {
 | 
							for _, field := range schema.Fields {
 | 
				
			||||||
			if field.DataType == "" && (field.Creatable || field.Updatable || field.Readable) {
 | 
								if field.DataType == "" && (field.Creatable || field.Updatable || field.Readable) {
 | 
				
			||||||
 | 
				
			|||||||
@ -50,6 +50,7 @@ type Statement struct {
 | 
				
			|||||||
type join struct {
 | 
					type join struct {
 | 
				
			||||||
	Name  string
 | 
						Name  string
 | 
				
			||||||
	Conds []interface{}
 | 
						Conds []interface{}
 | 
				
			||||||
 | 
						On    *clause.Where
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// StatementModifier statement modifier interface
 | 
					// StatementModifier statement modifier interface
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ require (
 | 
				
			|||||||
	gorm.io/driver/mysql v1.1.2
 | 
						gorm.io/driver/mysql v1.1.2
 | 
				
			||||||
	gorm.io/driver/postgres v1.1.0
 | 
						gorm.io/driver/postgres v1.1.0
 | 
				
			||||||
	gorm.io/driver/sqlite v1.1.4
 | 
						gorm.io/driver/sqlite v1.1.4
 | 
				
			||||||
	gorm.io/driver/sqlserver v1.0.8
 | 
						gorm.io/driver/sqlserver v1.0.9
 | 
				
			||||||
	gorm.io/gorm v1.21.14
 | 
						gorm.io/gorm v1.21.14
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -104,6 +104,27 @@ func TestJoinConds(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestJoinOn(t *testing.T) {
 | 
				
			||||||
 | 
						var user = *GetUser("joins-on", Config{Pets: 2})
 | 
				
			||||||
 | 
						DB.Save(&user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var user1 User
 | 
				
			||||||
 | 
						onQuery := DB.Where(&Pet{Name: "joins-on_pet_1"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := DB.Joins("NamedPet", onQuery).Where("users.name = ?", user.Name).First(&user1).Error; err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Failed to load with joins on, got error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AssertEqual(t, user1.NamedPet.Name, "joins-on_pet_1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						onQuery2 := DB.Where(&Pet{Name: "joins-on_pet_2"})
 | 
				
			||||||
 | 
						var user2 User
 | 
				
			||||||
 | 
						if err := DB.Joins("NamedPet", onQuery2).Where("users.name = ?", user.Name).First(&user2).Error; err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Failed to load with joins on, got error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						AssertEqual(t, user2.NamedPet.Name, "joins-on_pet_2")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestJoinsWithSelect(t *testing.T) {
 | 
					func TestJoinsWithSelect(t *testing.T) {
 | 
				
			||||||
	type result struct {
 | 
						type result struct {
 | 
				
			||||||
		ID    uint
 | 
							ID    uint
 | 
				
			||||||
 | 
				
			|||||||
@ -642,6 +642,40 @@ func TestSave(t *testing.T) {
 | 
				
			|||||||
	if !regexp.MustCompile("WHERE .id. = [^ ]+$").MatchString(stmt.SQL.String()) {
 | 
						if !regexp.MustCompile("WHERE .id. = [^ ]+$").MatchString(stmt.SQL.String()) {
 | 
				
			||||||
		t.Fatalf("invalid updating SQL, got %v", stmt.SQL.String())
 | 
							t.Fatalf("invalid updating SQL, got %v", stmt.SQL.String())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						user3 := *GetUser("save3", Config{})
 | 
				
			||||||
 | 
						DB.Create(&user3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := DB.First(&User{}, "name = ?", "save3").Error; err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find created user")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						user3.Name = "save3_"
 | 
				
			||||||
 | 
						if err := DB.Model(User{Model: user3.Model}).Save(&user3).Error; err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to save user, got %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var result2 User
 | 
				
			||||||
 | 
						if err := DB.First(&result2, "name = ?", "save3_").Error; err != nil || result2.ID != user3.ID {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find updated user, got %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := DB.Model(User{Model: user3.Model}).Save(&struct {
 | 
				
			||||||
 | 
							gorm.Model
 | 
				
			||||||
 | 
							Placeholder string
 | 
				
			||||||
 | 
							Name        string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							Model:       user3.Model,
 | 
				
			||||||
 | 
							Placeholder: "placeholder",
 | 
				
			||||||
 | 
							Name:        "save3__",
 | 
				
			||||||
 | 
						}).Error; err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to update user, got %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var result3 User
 | 
				
			||||||
 | 
						if err := DB.First(&result3, "name = ?", "save3__").Error; err != nil || result3.ID != user3.ID {
 | 
				
			||||||
 | 
							t.Fatalf("failed to find updated user")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestSaveWithPrimaryValue(t *testing.T) {
 | 
					func TestSaveWithPrimaryValue(t *testing.T) {
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,7 @@ import (
 | 
				
			|||||||
// He works in a Company (belongs to), he has a Manager (belongs to - single-table), and also managed a Team (has many - single-table)
 | 
					// He works in a Company (belongs to), he has a Manager (belongs to - single-table), and also managed a Team (has many - single-table)
 | 
				
			||||||
// He speaks many languages (many to many) and has many friends (many to many - single-table)
 | 
					// He speaks many languages (many to many) and has many friends (many to many - single-table)
 | 
				
			||||||
// His pet also has one Toy (has one - polymorphic)
 | 
					// His pet also has one Toy (has one - polymorphic)
 | 
				
			||||||
 | 
					// NamedPet is a reference to a Named `Pets` (has many)
 | 
				
			||||||
type User struct {
 | 
					type User struct {
 | 
				
			||||||
	gorm.Model
 | 
						gorm.Model
 | 
				
			||||||
	Name      string
 | 
						Name      string
 | 
				
			||||||
@ -18,6 +19,7 @@ type User struct {
 | 
				
			|||||||
	Birthday  *time.Time
 | 
						Birthday  *time.Time
 | 
				
			||||||
	Account   Account
 | 
						Account   Account
 | 
				
			||||||
	Pets      []*Pet
 | 
						Pets      []*Pet
 | 
				
			||||||
 | 
						NamedPet  *Pet
 | 
				
			||||||
	Toys      []Toy `gorm:"polymorphic:Owner"`
 | 
						Toys      []Toy `gorm:"polymorphic:Owner"`
 | 
				
			||||||
	CompanyID *int
 | 
						CompanyID *int
 | 
				
			||||||
	Company   Company
 | 
						Company   Company
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user