Repository: go-gorm/gorm
Pull request #6137 (2023-03-10 10:17:22)

feat: support embedded preload
<!--
Make sure these boxes checked before submitting your pull request.

For significant changes, please open an issue to make an agreement on an implementation design/plan first before starting it.
-->

- [X] Do only one thing
- [X] Non breaking API changes
- [X] Tested

### What did this pull request do?

<!--
provide a general description of the code changes in your pull request
-->
Support embedded preload, especially the same structure. see #6131 

Unimplemented:
- Polymorphism has_one/has_many in repeating embedded struct // Their polymorphic value in db are all the same.

### User Case Description

<!-- Your use case -->
```go
type Country struct {
	ID   int `gorm:"primaryKey"`
	Name string
}
type Address struct {
	CountryID int
	Country   Country
}
type NestedAddress struct {
	Address
}
type Org struct {
	ID              int
	PostalAddress   Address `gorm:"embedded;embeddedPrefix:postal_address_"`
	VisitingAddress Address `gorm:"embedded;embeddedPrefix:visiting_address_"`
	AddressID       int
	Address         struct {
		ID int
		Address
	}
	NestedAddress *NestedAddress `gorm:"embedded;embeddedPrefix:nested_address_"`
}

// Only preload Org.Address and Org.Address.Country
db.Preload("Address.Country")  // "Address" is has_one, "Country" is belongs_to (nested relation)

// Only preload Org.VisitingAddress
db.Preload("PostalAddress.Country") // "PostalAddress.Country" is belongs_to (embedded relation)

// Only preload Org.NestedAddress
db.Preload("NestedAddress.Address.Country") // "NestedAddress.Address.Country" is belongs_to (embedded relation)

// All preloaded include "Address" but exclude "Address.Country", because it won’t preload nested associations.
db.Preload(clause.Associations)
```



Patch:
diff --git a/.github/workflows/invalid_question.yml b/.github/workflows/invalid_question.yml
index 77b26ab..fbebfc1 100644
--- a/.github/workflows/invalid_question.yml
+++ b/.github/workflows/invalid_question.yml
@@ -16,7 +16,7 @@ jobs:
       ACTIONS_STEP_DEBUG: true
     steps:
     - name: Close Stale Issues
-      uses: actions/stale@v7
+      uses: actions/stale@v8
       with:
         repo-token: ${{ secrets.GITHUB_TOKEN }}
         stale-issue-message: "This issue has been marked as invalid question, please give more information by following the `Question` template, if you believe there is a bug of GORM, please create a pull request that could reproduce the issue on [https://github.com/go-gorm/playground](https://github.com/go-gorm/playground), the issue will be closed in 30 days if no further activity occurs. most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.io ✨ [Search Before Asking](https://stackoverflow.com/help/how-to-ask) ✨"
diff --git a/.github/workflows/missing_playground.yml b/.github/workflows/missing_playground.yml
index 1efa361..b23a5bf 100644
--- a/.github/workflows/missing_playground.yml
+++ b/.github/workflows/missing_playground.yml
@@ -16,7 +16,7 @@ jobs:
       ACTIONS_STEP_DEBUG: true
     steps:
     - name: Close Stale Issues
-      uses: actions/stale@v7
+      uses: actions/stale@v8
       with:
         repo-token: ${{ secrets.GITHUB_TOKEN }}
         stale-issue-message: "The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout [https://github.com/go-gorm/playground](https://github.com/go-gorm/playground) for details. it will be closed in 30 days if no further activity occurs. if you are asking question, please use the `Question` template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.io ✨ [Search Before Asking](https://stackoverflow.com/help/how-to-ask) ✨"
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 43f2f73..c975288 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -16,7 +16,7 @@ jobs:
       ACTIONS_STEP_DEBUG: true
     steps:
     - name: Close Stale Issues
-      uses: actions/stale@v7
+      uses: actions/stale@v8
       with:
         repo-token: ${{ secrets.GITHUB_TOKEN }}
         stale-issue-message: "This issue has been automatically marked as stale because it has been open 360 days with no activity. Remove stale label or comment or this will be closed in 180 days"
diff --git a/callbacks.go b/callbacks.go
index de979e4..ca6b6d5 100644
--- a/callbacks.go
+++ b/callbacks.go
@@ -75,11 +75,7 @@ func (cs *callbacks) Raw() *processor {
 func (p *processor) Execute(db *DB) *DB {
 	// call scopes
 	for len(db.Statement.scopes) > 0 {
-		scopes := db.Statement.scopes
-		db.Statement.scopes = nil
-		for _, scope := range scopes {
-			db = scope(db)
-		}
+		db = db.executeScopes()
 	}
 
 	var (
diff --git a/callbacks/associations.go b/callbacks/associations.go
index 9d7c141..f3cd464 100644
--- a/callbacks/associations.go
+++ b/callbacks/associations.go
@@ -51,25 +51,40 @@ func SaveBeforeAssociations(create bool) func(db *gorm.DB) {
 					}
 
 					elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
+					distinctElems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
+					identityMap := map[string]bool{}
 					for i := 0; i < rValLen; i++ {
 						obj := db.Statement.ReflectValue.Index(i)
 						if reflect.Indirect(obj).Kind() != reflect.Struct {
 							break
 						}
-
 						if _, zero := rel.Field.ValueOf(db.Statement.Context, obj); !zero { // check belongs to relation value
 							rv := rel.Field.ReflectValueOf(db.Statement.Context, obj) // relation reflect value
+							if !isPtr {
+								rv = rv.Addr()
+							}
 							objs = append(objs, obj)
-							if isPtr {
-								elems = reflect.Append(elems, rv)
-							} else {
-								elems = reflect.Append(elems, rv.Addr())
+							elems = reflect.Append(elems, rv)
+
+							relPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields))
+							for _, pf := range rel.FieldSchema.PrimaryFields {
+								if pfv, ok := pf.ValueOf(db.Statement.Context, rv); !ok {
+									relPrimaryValues = append(relPrimaryValues, pfv)
+								}
+							}
+							cacheKey := utils.ToStringKey(relPrimaryValues...)
+							if len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {
+								if cacheKey != "" { // has primary fields
+									identityMap[cacheKey] = true
+								}
+
+								distinctElems = reflect.Append(distinctElems, rv)
 							}
 						}
 					}
 
 					if elems.Len() > 0 {
-						if saveAssociations(db, rel, elems, selectColumns, restricted, nil) == nil {
+						if saveAssociations(db, rel, distinctElems, selectColumns, restricted, nil) == nil {
 							for i := 0; i < elems.Len(); i++ {
 								setupReferences(objs[i], elems.Index(i))
 							}
diff --git a/callbacks/preload.go b/callbacks/preload.go
index ea2570b..15669c8 100644
--- a/callbacks/preload.go
+++ b/callbacks/preload.go
@@ -3,6 +3,7 @@ package callbacks
 import (
 	"fmt"
 	"reflect"
+	"strings"
 
 	"gorm.io/gorm"
 	"gorm.io/gorm/clause"
@@ -10,6 +11,98 @@ import (
 	"gorm.io/gorm/utils"
 )
 
+// parsePreloadMap extracts nested preloads. e.g.
+//
+//	// schema has a "k0" relation and a "k7.k8" embedded relation
+//	parsePreloadMap(schema, map[string][]interface{}{
+//		clause.Associations: {"arg1"},
+//		"k1":                {"arg2"},
+//		"k2.k3":             {"arg3"},
+//		"k4.k5.k6":          {"arg4"},
+//	})
+//	// preloadMap is
+//	map[string]map[string][]interface{}{
+//		"k0": {},
+//		"k7": {
+//			"k8": {},
+//		},
+//		"k1": {},
+//		"k2": {
+//			"k3": {"arg3"},
+//		},
+//		"k4": {
+//			"k5.k6": {"arg4"},
+//		},
+//	}
+func parsePreloadMap(s *schema.Schema, preloads map[string][]interface{}) map[string]map[string][]interface{} {
+	preloadMap := map[string]map[string][]interface{}{}
+	setPreloadMap := func(name, value string, args []interface{}) {
+		if _, ok := preloadMap[name]; !ok {
+			preloadMap[name] = map[string][]interface{}{}
+		}
+		if value != "" {
+			preloadMap[name][value] = args
+		}
+	}
+
+	for name, args := range preloads {
+		preloadFields := strings.Split(name, ".")
+		value := strings.TrimPrefix(strings.TrimPrefix(name, preloadFields[0]), ".")
+		if preloadFields[0] == clause.Associations {
+			for _, relation := range s.Relationships.Relations {
+				if relation.Schema == s {
+					setPreloadMap(relation.Name, value, args)
+				}
+			}
+
+			for embedded, embeddedRelations := range s.Relationships.EmbeddedRelations {
+				for _, value := range embeddedValues(embeddedRelations) {
+					setPreloadMap(embedded, value, args)
+				}
+			}
+		} else {
+			setPreloadMap(preloadFields[0], value, args)
+		}
+	}
+	return preloadMap
+}
+
+func embeddedValues(embeddedRelations *schema.Relationships) []string {
+	if embeddedRelations == nil {
+		return nil
+	}
+	names := make([]string, 0, len(embeddedRelations.Relations)+len(embeddedRelations.EmbeddedRelations))
+	for _, relation := range embeddedRelations.Relations {
+		// skip first struct name
+		names = append(names, strings.Join(relation.Field.BindNames[1:], "."))
+	}
+	for _, relations := range embeddedRelations.EmbeddedRelations {
+		names = append(names, embeddedValues(relations)...)
+	}
+	return names
+}
+
+func preloadEmbedded(tx *gorm.DB, relationships *schema.Relationships, s *schema.Schema, preloads map[string][]interface{}, as []interface{}) error {
+	if relationships == nil {
+		return nil
+	}
+	preloadMap := parsePreloadMap(s, preloads)
+	for name := range preloadMap {
+		if embeddedRelations := relationships.EmbeddedRelations[name]; embeddedRelations != nil {
+			if err := preloadEmbedded(tx, embeddedRelations, s, preloadMap[name], as); err != nil {
+				return err
+			}
+		} else if rel := relationships.Relations[name]; rel != nil {
+			if err := preload(tx, rel, append(preloads[name], as), preloadMap[name]); err != nil {
+				return err
+			}
+		} else {
+			return fmt.Errorf("%s: %w (embedded) for schema %s", name, gorm.ErrUnsupportedRelation, s.Name)
+		}
+	}
+	return nil
+}
+
 func preload(tx *gorm.DB, rel *schema.Relationship, conds []interface{}, preloads map[string][]interface{}) error {
 	var (
 		reflectValue     = tx.Statement.ReflectValue
diff --git a/callbacks/query.go b/callbacks/query.go
index c87f17b..95db1f0 100644
--- a/callbacks/query.go
+++ b/callbacks/query.go
@@ -267,32 +267,7 @@ func Preload(db *gorm.DB) {
 			return
 		}
 
-		preloadMap := map[string]map[string][]interface{}{}
-		for name := range db.Statement.Preloads {
-			preloadFields := strings.Split(name, ".")
-			if preloadFields[0] == clause.Associations {
-				for _, rel := range db.Statement.Schema.Relationships.Relations {
-					if rel.Schema == db.Statement.Schema {
-						if _, ok := preloadMap[rel.Name]; !ok {
-							preloadMap[rel.Name] = map[string][]interface{}{}
-						}
-
-						if value := strings.TrimPrefix(strings.TrimPrefix(name, preloadFields[0]), "."); value != "" {
-							preloadMap[rel.Name][value] = db.Statement.Preloads[name]
-						}
-					}
-				}
-			} else {
-				if _, ok := preloadMap[preloadFields[0]]; !ok {
-					preloadMap[preloadFields[0]] = map[string][]interface{}{}
-				}
-
-				if value := strings.TrimPrefix(strings.TrimPrefix(name, preloadFields[0]), "."); value != "" {
-					preloadMap[preloadFields[0]][value] = db.Statement.Preloads[name]
-				}
-			}
-		}
-
+		preloadMap := parsePreloadMap(db.Statement.Schema, db.Statement.Preloads)
 		preloadNames := make([]string, 0, len(preloadMap))
 		for key := range preloadMap {
 			preloadNames = append(preloadNames, key)
@@ -312,7 +287,9 @@ func Preload(db *gorm.DB) {
 		preloadDB.Statement.Unscoped = db.Statement.Unscoped
 
 		for _, name := range preloadNames {
-			if rel := preloadDB.Statement.Schema.Relationships.Relations[name]; rel != nil {
+			if relations := preloadDB.Statement.Schema.Relationships.EmbeddedRelations[name]; relations != nil {
+				db.AddError(preloadEmbedded(preloadDB.Table("").Session(&gorm.Session{Context: db.Statement.Context, SkipHooks: db.Statement.SkipHooks}), relations, db.Statement.Schema, preloadMap[name], db.Statement.Preloads[clause.Associations]))
+			} else if rel := preloadDB.Statement.Schema.Relationships.Relations[name]; rel != nil {
 				db.AddError(preload(preloadDB.Table("").Session(&gorm.Session{Context: db.Statement.Context, SkipHooks: db.Statement.SkipHooks}), rel, append(db.Statement.Preloads[name], db.Statement.Preloads[clause.Associations]...), preloadMap[name]))
 			} else {
 				db.AddError(fmt.Errorf("%s: %w for schema %s", name, gorm.ErrUnsupportedRelation, db.Statement.Schema.Name))
diff --git a/chainable_api.go b/chainable_api.go
index a85235e..19d405c 100644
--- a/chainable_api.go
+++ b/chainable_api.go
@@ -366,6 +366,36 @@ func (db *DB) Scopes(funcs ...func(*DB) *DB) (tx *DB) {
 	return tx
 }
 
+func (db *DB) executeScopes() (tx *DB) {
+	tx = db.getInstance()
+	scopes := db.Statement.scopes
+	if len(scopes) == 0 {
+		return tx
+	}
+	tx.Statement.scopes = nil
+
+	conditions := make([]clause.Interface, 0, 4)
+	if cs, ok := tx.Statement.Clauses["WHERE"]; ok && cs.Expression != nil {
+		conditions = append(conditions, cs.Expression.(clause.Interface))
+		cs.Expression = nil
+		tx.Statement.Clauses["WHERE"] = cs
+	}
+
+	for _, scope := range scopes {
+		tx = scope(tx)
+		if cs, ok := tx.Statement.Clauses["WHERE"]; ok && cs.Expression != nil {
+			conditions = append(conditions, cs.Expression.(clause.Interface))
+			cs.Expression = nil
+			tx.Statement.Clauses["WHERE"] = cs
+		}
+	}
+
+	for _, condition := range conditions {
+		tx.Statement.AddClause(condition)
+	}
+	return tx
+}
+
 // Preload preload associations with given conditions
 //
 //	// get all users, and preload all non-cancelled orders
diff --git a/clause/limit.go b/clause/limit.go
index 3ede738..abda005 100644
--- a/clause/limit.go
+++ b/clause/limit.go
@@ -33,7 +33,7 @@ func (limit Limit) MergeClause(clause *Clause) {
 	clause.Name = ""
 
 	if v, ok := clause.Expression.(Limit); ok {
-		if (limit.Limit == nil || *limit.Limit == 0) && (v.Limit != nil && *v.Limit != 0) {
+		if (limit.Limit == nil || *limit.Limit == 0) && v.Limit != nil {
 			limit.Limit = v.Limit
 		}
 
diff --git a/finisher_api.go b/finisher_api.go
index e6fe466..0e26f18 100644
--- a/finisher_api.go
+++ b/finisher_api.go
@@ -35,9 +35,10 @@ func (db *DB) CreateInBatches(value interface{}, batchSize int) (tx *DB) {
 		var rowsAffected int64
 		tx = db.getInstance()
 
+		// the reflection length judgment of the optimized value
+		reflectLen := reflectValue.Len()
+
 		callFc := func(tx *DB) error {
-			// the reflection length judgment of the optimized value
-			reflectLen := reflectValue.Len()
 			for i := 0; i < reflectLen; i += batchSize {
 				ends := i + batchSize
 				if ends > reflectLen {
@@ -55,7 +56,7 @@ func (db *DB) CreateInBatches(value interface{}, batchSize int) (tx *DB) {
 			return nil
 		}
 
-		if tx.SkipDefaultTransaction {
+		if tx.SkipDefaultTransaction || reflectLen <= batchSize {
 			tx.AddError(callFc(tx.Session(&Session{})))
 		} else {
 			tx.AddError(tx.Transaction(callFc))
@@ -106,7 +107,7 @@ func (db *DB) Save(value interface{}) (tx *DB) {
 		updateTx := tx.callbacks.Update().Execute(tx.Session(&Session{Initialized: true}))
 
 		if updateTx.Error == nil && updateTx.RowsAffected == 0 && !updateTx.DryRun && !selectedUpdate {
-			return tx.Create(value)
+			return tx.Clauses(clause.OnConflict{UpdateAll: true}).Create(value)
 		}
 
 		return updateTx
@@ -491,7 +492,7 @@ func (db *DB) Count(count *int64) (tx *DB) {
 	tx.Statement.Dest = count
 	tx = tx.callbacks.Query().Execute(tx)
 
-	if tx.RowsAffected != 1 {
+	if _, ok := db.Statement.Clauses["GROUP BY"]; ok || tx.RowsAffected != 1 {
 		*count = tx.RowsAffected
 	}
 
diff --git a/gorm.go b/gorm.go
index 9a70c3d..4402a2d 100644
--- a/gorm.go
+++ b/gorm.go
@@ -47,6 +47,8 @@ type Config struct {
 	QueryFields bool
 	// CreateBatchSize default create batch size
 	CreateBatchSize int
+	// TranslateError enabling error translation
+	TranslateError bool
 
 	// ClauseBuilders clause builder
 	ClauseBuilders map[string]clause.ClauseBuilder
@@ -348,8 +350,10 @@ func (db *DB) Callback() *callbacks {
 // AddError add error to db
 func (db *DB) AddError(err error) error {
 	if err != nil {
-		if errTranslator, ok := db.Dialector.(ErrorTranslator); ok {
-			err = errTranslator.Translate(err)
+		if db.Config.TranslateError {
+			if errTranslator, ok := db.Dialector.(ErrorTranslator); ok {
+				err = errTranslator.Translate(err)
+			}
 		}
 
 		if db.Error == nil {
diff --git a/migrator.go b/migrator.go
index 9c7cc2c..037afc3 100644
--- a/migrator.go
+++ b/migrator.go
@@ -13,11 +13,7 @@ func (db *DB) Migrator() Migrator {
 
 	// apply scopes to migrator
 	for len(tx.Statement.scopes) > 0 {
-		scopes := tx.Statement.scopes
-		tx.Statement.scopes = nil
-		for _, scope := range scopes {
-			tx = scope(tx)
-		}
+		tx = tx.executeScopes()
 	}
 
 	return tx.Dialector.Migrator(tx.Session(&Session{}))
diff --git a/migrator/index.go b/migrator/index.go
index fe686e5..8845da9 100644
--- a/migrator/index.go
+++ b/migrator/index.go
@@ -17,12 +17,12 @@ func (idx Index) Table() string {
 	return idx.TableName
 }
 
-// Name return the name  of the index.
+// Name return the name of the index.
 func (idx Index) Name() string {
 	return idx.NameValue
 }
 
-// Columns return the columns fo the index
+// Columns return the columns of the index
 func (idx Index) Columns() []string {
 	return idx.ColumnList
 }
@@ -37,7 +37,7 @@ func (idx Index) Unique() (unique bool, ok bool) {
 	return idx.UniqueValue.Bool, idx.UniqueValue.Valid
 }
 
-// Option return the optional attribute fo the index
+// Option return the optional attribute of the index
 func (idx Index) Option() string {
 	return idx.OptionValue
 }
diff --git a/migrator/migrator.go b/migrator/migrator.go
index 389ce00..32c6a05 100644
--- a/migrator/migrator.go
+++ b/migrator/migrator.go
@@ -113,7 +113,7 @@ func (m Migrator) AutoMigrate(values ...interface{}) error {
 				return err
 			}
 		} else {
-			if err := m.RunWithValue(value, func(stmt *gorm.Statement) (errr error) {
+			if err := m.RunWithValue(value, func(stmt *gorm.Statement) error {
 				columnTypes, err := queryTx.Migrator().ColumnTypes(value)
 				if err != nil {
 					return err
@@ -123,7 +123,6 @@ func (m Migrator) AutoMigrate(values ...interface{}) error {
 					parseCheckConstraints = stmt.Schema.ParseCheckConstraints()
 				)
 				for _, dbName := range stmt.Schema.DBNames {
-					field := stmt.Schema.FieldsByDBName[dbName]
 					var foundColumn gorm.ColumnType
 
 					for _, columnType := range columnTypes {
@@ -135,12 +134,15 @@ func (m Migrator) AutoMigrate(values ...interface{}) error {
 
 					if foundColumn == nil {
 						// not found, add column
-						if err := execTx.Migrator().AddColumn(value, dbName); err != nil {
+						if err = execTx.Migrator().AddColumn(value, dbName); err != nil {
+							return err
+						}
+					} else {
+						// found, smartly migrate
+						field := stmt.Schema.FieldsByDBName[dbName]
+						if err = execTx.Migrator().MigrateColumn(value, field, foundColumn); err != nil {
 							return err
 						}
-					} else if err := execTx.Migrator().MigrateColumn(value, field, foundColumn); err != nil {
-						// found, smart migrate
-						return err
 					}
 				}
 
@@ -195,7 +197,7 @@ func (m Migrator) GetTables() (tableList []string, err error) {
 func (m Migrator) CreateTable(values ...interface{}) error {
 	for _, value := range m.ReorderModels(values, false) {
 		tx := m.DB.Session(&gorm.Session{})
-		if err := m.RunWithValue(value, func(stmt *gorm.Statement) (errr error) {
+		if err := m.RunWithValue(value, func(stmt *gorm.Statement) (err error) {
 			var (
 				createTableSQL          = "CREATE TABLE ? ("
 				values                  = []interface{}{m.CurrentTable(stmt)}
@@ -214,7 +216,7 @@ func (m Migrator) CreateTable(values ...interface{}) error {
 
 			if !hasPrimaryKeyInDataType && len(stmt.Schema.PrimaryFields) > 0 {
 				createTableSQL += "PRIMARY KEY ?,"
-				primaryKeys := []interface{}{}
+				primaryKeys := make([]interface{}, 0, len(stmt.Schema.PrimaryFields))
 				for _, field := range stmt.Schema.PrimaryFields {
 					primaryKeys = append(primaryKeys, clause.Column{Name: field.DBName})
 				}
@@ -225,8 +227,8 @@ func (m Migrator) CreateTable(values ...interface{}) error {
 			for _, idx := range stmt.Schema.ParseIndexes() {
 				if m.CreateIndexAfterCreateTable {
 					defer func(value interface{}, name string) {
-						if errr == nil {
-							errr = tx.Migrator().CreateIndex(value, name)
+						if err == nil {
+							err = tx.Migrator().CreateIndex(value, name)
 						}
 					}(value, idx.Name)
 				} else {
@@ -276,8 +278,8 @@ func (m Migrator) CreateTable(values ...interface{}) error {
 				createTableSQL += fmt.Sprint(tableOption)
 			}
 
-			errr = tx.Exec(createTableSQL, values...).Error
-			return errr
+			err = tx.Exec(createTableSQL, values...).Error
+			return err
 		}); err != nil {
 			return err
 		}
@@ -498,7 +500,7 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
 		currentDefaultNotNull := field.HasDefaultValue && (field.DefaultValueInterface != nil || !strings.EqualFold(field.DefaultValue, "NULL"))
 		dv, dvNotNull := columnType.DefaultValue()
 		if dvNotNull && !currentDefaultNotNull {
-			// defalut value -> null
+			// default value -> null
 			alterColumn = true
 		} else if !dvNotNull && currentDefaultNotNull {
 			// null -> default value
diff --git a/schema/field.go b/schema/field.go
index 00beb06..b5103d5 100644
--- a/schema/field.go
+++ b/schema/field.go
@@ -89,6 +89,10 @@ type Field struct {
 	NewValuePool           FieldNewValuePool
 }
 
+func (field *Field) BindName() string {
+	return strings.Join(field.BindNames, ".")
+}
+
 // ParseField parses reflect.StructField to Field
 func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
 	var (
@@ -580,8 +584,6 @@ func (field *Field) setupValuerAndSetter() {
 			case **bool:
 				if data != nil && *data != nil {
 					field.ReflectValueOf(ctx, value).SetBool(**data)
-				} else {
-					field.ReflectValueOf(ctx, value).SetBool(false)
 				}
 			case bool:
 				field.ReflectValueOf(ctx, value).SetBool(data)
@@ -601,8 +603,6 @@ func (field *Field) setupValuerAndSetter() {
 			case **int64:
 				if data != nil && *data != nil {
 					field.ReflectValueOf(ctx, value).SetInt(**data)
-				} else {
-					field.ReflectValueOf(ctx, value).SetInt(0)
 				}
 			case int64:
 				field.ReflectValueOf(ctx, value).SetInt(data)
@@ -667,8 +667,6 @@ func (field *Field) setupValuerAndSetter() {
 			case **uint64:
 				if data != nil && *data != nil {
 					field.ReflectValueOf(ctx, value).SetUint(**data)
-				} else {
-					field.ReflectValueOf(ctx, value).SetUint(0)
 				}
 			case uint64:
 				field.ReflectValueOf(ctx, value).SetUint(data)
@@ -721,8 +719,6 @@ func (field *Field) setupValuerAndSetter() {
 			case **float64:
 				if data != nil && *data != nil {
 					field.ReflectValueOf(ctx, value).SetFloat(**data)
-				} else {
-					field.ReflectValueOf(ctx, value).SetFloat(0)
 				}
 			case float64:
 				field.ReflectValueOf(ctx, value).SetFloat(data)
@@ -767,8 +763,6 @@ func (field *Field) setupValuerAndSetter() {
 			case **string:
 				if data != nil && *data != nil {
 					field.ReflectValueOf(ctx, value).SetString(**data)
-				} else {
-					field.ReflectValueOf(ctx, value).SetString("")
 				}
 			case string:
 				field.ReflectValueOf(ctx, value).SetString(data)
diff --git a/schema/relationship.go b/schema/relationship.go
index b33b94a..e03dcc5 100644
--- a/schema/relationship.go
+++ b/schema/relationship.go
@@ -27,6 +27,8 @@ type Relationships struct {
 	HasMany   []*Relationship
 	Many2Many []*Relationship
 	Relations map[string]*Relationship
+
+	EmbeddedRelations map[string]*Relationships
 }
 
 type Relationship struct {
@@ -106,7 +108,7 @@ func (schema *Schema) parseRelation(field *Field) *Relationship {
 	}
 
 	if schema.err == nil {
-		schema.Relationships.Relations[relation.Name] = relation
+		schema.setRelation(relation)
 		switch relation.Type {
 		case HasOne:
 			schema.Relationships.HasOne = append(schema.Relationships.HasOne, relation)
@@ -122,6 +124,39 @@ func (schema *Schema) parseRelation(field *Field) *Relationship {
 	return relation
 }
 
+func (schema *Schema) setRelation(relation *Relationship) {
+	// set non-embedded relation
+	if rel := schema.Relationships.Relations[relation.Name]; rel != nil {
+		if len(rel.Field.BindNames) > 1 {
+			schema.Relationships.Relations[relation.Name] = relation
+		}
+	} else {
+		schema.Relationships.Relations[relation.Name] = relation
+	}
+
+	// set embedded relation
+	if len(relation.Field.BindNames) <= 1 {
+		return
+	}
+	relationships := &schema.Relationships
+	for i, name := range relation.Field.BindNames {
+		if i < len(relation.Field.BindNames)-1 {
+			if relationships.EmbeddedRelations == nil {
+				relationships.EmbeddedRelations = map[string]*Relationships{}
+			}
+			if r := relationships.EmbeddedRelations[name]; r == nil {
+				relationships.EmbeddedRelations[name] = &Relationships{}
+			}
+			relationships = relationships.EmbeddedRelations[name]
+		} else {
+			if relationships.Relations == nil {
+				relationships.Relations = map[string]*Relationship{}
+			}
+			relationships.Relations[relation.Name] = relation
+		}
+	}
+}
+
 // User has many Toys, its `Polymorphic` is `Owner`, Pet has one Toy, its `Polymorphic` is `Owner`
 //
 //	type User struct {
@@ -166,6 +201,11 @@ func (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Fi
 			}
 		}
 
+		if primaryKeyField == nil {
+			schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing primaryKey field", relation.FieldSchema, schema, field.Name)
+			return
+		}
+
 		// use same data type for foreign keys
 		if copyableDataType(primaryKeyField.DataType) {
 			relation.Polymorphic.PolymorphicID.DataType = primaryKeyField.DataType
@@ -443,6 +483,7 @@ func (schema *Schema) guessRelation(relation *Relationship, field *Field, cgl gu
 			primaryFields = primarySchema.PrimaryFields
 		}
 
+	primaryFieldLoop:
 		for _, primaryField := range primaryFields {
 			lookUpName := primarySchemaName + primaryField.Name
 			if gl == guessBelongs {
@@ -454,11 +495,18 @@ func (schema *Schema) guessRelation(relation *Relationship, field *Field, cgl gu
 				lookUpNames = append(lookUpNames, strings.TrimSuffix(lookUpName, primaryField.Name)+"ID", strings.TrimSuffix(lookUpName, primaryField.Name)+"Id", schema.namer.ColumnName(foreignSchema.Table, strings.TrimSuffix(lookUpName, primaryField.Name)+"ID"))
 			}
 
+			for _, name := range lookUpNames {
+				if f := foreignSchema.LookUpFieldByBindName(field.BindNames, name); f != nil {
+					foreignFields = append(foreignFields, f)
+					primaryFields = append(primaryFields, primaryField)
+					continue primaryFieldLoop
+				}
+			}
 			for _, name := range lookUpNames {
 				if f := foreignSchema.LookUpField(name); f != nil {
 					foreignFields = append(foreignFields, f)
 					primaryFields = append(primaryFields, primaryField)
-					break
+					continue primaryFieldLoop
 				}
 			}
 		}
diff --git a/schema/schema.go b/schema/schema.go
index 17bdb25..e13a5ed 100644
--- a/schema/schema.go
+++ b/schema/schema.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"go/ast"
 	"reflect"
+	"strings"
 	"sync"
 
 	"gorm.io/gorm/clause"
@@ -25,6 +26,7 @@ type Schema struct {
 	PrimaryFieldDBNames       []string
 	Fields                    []*Field
 	FieldsByName              map[string]*Field
+	FieldsByBindName          map[string]*Field // embedded fields is 'Embed.Field'
 	FieldsByDBName            map[string]*Field
 	FieldsWithDefaultDBValue  []*Field // fields with default value assigned by database
 	Relationships             Relationships
@@ -67,6 +69,27 @@ func (schema Schema) LookUpField(name string) *Field {
 	return nil
 }
 
+// LookUpFieldByBindName looks for the closest field in the embedded struct.
+//
+//	type Struct struct {
+//		Embedded struct {
+//			ID string // is selected by LookUpFieldByBindName([]string{"Embedded", "ID"}, "ID")
+//		}
+//		ID string // is selected by LookUpFieldByBindName([]string{"ID"}, "ID")
+//	}
+func (schema Schema) LookUpFieldByBindName(bindNames []string, name string) *Field {
+	if len(bindNames) == 0 {
+		return nil
+	}
+	for i := len(bindNames) - 1; i >= 0; i-- {
+		find := strings.Join(bindNames[:i], ".") + "." + name
+		if field, ok := schema.FieldsByBindName[find]; ok {
+			return field
+		}
+	}
+	return nil
+}
+
 type Tabler interface {
 	TableName() string
 }
@@ -140,15 +163,16 @@ func ParseWithSpecialTableName(dest interface{}, cacheStore *sync.Map, namer Nam
 	}
 
 	schema := &Schema{
-		Name:           modelType.Name(),
-		ModelType:      modelType,
-		Table:          tableName,
-		FieldsByName:   map[string]*Field{},
-		FieldsByDBName: map[string]*Field{},
-		Relationships:  Relationships{Relations: map[string]*Relationship{}},
-		cacheStore:     cacheStore,
-		namer:          namer,
-		initialized:    make(chan struct{}),
+		Name:             modelType.Name(),
+		ModelType:        modelType,
+		Table:            tableName,
+		FieldsByName:     map[string]*Field{},
+		FieldsByBindName: map[string]*Field{},
+		FieldsByDBName:   map[string]*Field{},
+		Relationships:    Relationships{Relations: map[string]*Relationship{}},
+		cacheStore:       cacheStore,
+		namer:            namer,
+		initialized:      make(chan struct{}),
 	}
 	// When the schema initialization is completed, the channel will be closed
 	defer close(schema.initialized)
@@ -176,6 +200,7 @@ func ParseWithSpecialTableName(dest interface{}, cacheStore *sync.Map, namer Nam
 			field.DBName = namer.ColumnName(schema.Table, field.Name)
 		}
 
+		bindName := field.BindName()
 		if field.DBName != "" {
 			// nonexistence or shortest path or first appear prioritized if has permission
 			if v, ok := schema.FieldsByDBName[field.DBName]; !ok || ((field.Creatable || field.Updatable || field.Readable) && len(field.BindNames) < len(v.BindNames)) {
@@ -184,6 +209,7 @@ func ParseWithSpecialTableName(dest interface{}, cacheStore *sync.Map, namer Nam
 				}
 				schema.FieldsByDBName[field.DBName] = field
 				schema.FieldsByName[field.Name] = field
+				schema.FieldsByBindName[bindName] = field
 
 				if v != nil && v.PrimaryKey {
 					for idx, f := range schema.PrimaryFields {
@@ -202,6 +228,9 @@ func ParseWithSpecialTableName(dest interface{}, cacheStore *sync.Map, namer Nam
 		if of, ok := schema.FieldsByName[field.Name]; !ok || of.TagSettings["-"] == "-" {
 			schema.FieldsByName[field.Name] = field
 		}
+		if of, ok := schema.FieldsByBindName[bindName]; !ok || of.TagSettings["-"] == "-" {
+			schema.FieldsByBindName[bindName] = field
+		}
 
 		field.setupValuerAndSetter()
 	}
@@ -293,6 +322,7 @@ func ParseWithSpecialTableName(dest interface{}, cacheStore *sync.Map, namer Nam
 					return schema, schema.err
 				} else {
 					schema.FieldsByName[field.Name] = field
+					schema.FieldsByBindName[field.BindName()] = field
 				}
 			}
 
diff --git a/statement.go b/statement.go
index bc959f0..59c0b77 100644
--- a/statement.go
+++ b/statement.go
@@ -324,11 +324,9 @@ func (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) []
 		case clause.Expression:
 			conds = append(conds, v)
 		case *DB:
-			for _, scope := range v.Statement.scopes {
-				v = scope(v)
-			}
+			v.executeScopes()
 
-			if cs, ok := v.Statement.Clauses["WHERE"]; ok {
+			if cs, ok := v.Statement.Clauses["WHERE"]; ok && cs.Expression != nil {
 				if where, ok := cs.Expression.(clause.Where); ok {
 					if len(where.Exprs) == 1 {
 						if orConds, ok := where.Exprs[0].(clause.OrConditions); ok {
@@ -336,9 +334,13 @@ func (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) []
 						}
 					}
 					conds = append(conds, clause.And(where.Exprs...))
-				} else if cs.Expression != nil {
+				} else {
 					conds = append(conds, cs.Expression)
 				}
+				if v.Statement == stmt {
+					cs.Expression = nil
+					stmt.Statement.Clauses["WHERE"] = cs
+				}
 			}
 		case map[interface{}]interface{}:
 			for i, j := range v {


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index cfe8e56..bf225d4 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -22,7 +22,7 @@ jobs:
 
     steps:
     - name: Set up Go 1.x
-      uses: actions/setup-go@v3
+      uses: actions/setup-go@v4
       with:
         go-version: ${{ matrix.go }}
 
@@ -65,7 +65,7 @@ jobs:
 
     steps:
     - name: Set up Go 1.x
-      uses: actions/setup-go@v3
+      uses: actions/setup-go@v4
       with:
         go-version: ${{ matrix.go }}
 
@@ -109,7 +109,7 @@ jobs:
 
     steps:
     - name: Set up Go 1.x
-      uses: actions/setup-go@v3
+      uses: actions/setup-go@v4
       with:
         go-version: ${{ matrix.go }}
 
@@ -152,7 +152,7 @@ jobs:
 
     steps:
     - name: Set up Go 1.x
-      uses: actions/setup-go@v3
+      uses: actions/setup-go@v4
       with:
         go-version: ${{ matrix.go }}
 
@@ -184,7 +184,7 @@ jobs:
           version: ${{matrix.dbversion}}
 
       - name: Set up Go 1.x
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4
         with:
           go-version: ${{ matrix.go }}
 
diff --git a/clause/limit_test.go b/clause/limit_test.go
index 79065ab..a9fd4e2 100644
--- a/clause/limit_test.go
+++ b/clause/limit_test.go
@@ -28,6 +28,10 @@ func TestLimit(t *testing.T) {
 			[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit0}},
 			"SELECT * FROM `users` LIMIT 0", nil,
 		},
+		{
+			[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit0}, clause.Limit{Offset: 0}},
+			"SELECT * FROM `users` LIMIT 0", nil,
+		},
 		{
 			[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Offset: 20}},
 			"SELECT * FROM `users` OFFSET 20", nil,
diff --git a/schema/relationship_test.go b/schema/relationship_test.go
index 85c4558..732f6f7 100644
--- a/schema/relationship_test.go
+++ b/schema/relationship_test.go
@@ -518,6 +518,132 @@ func TestEmbeddedRelation(t *testing.T) {
 	}
 }
 
+func TestEmbeddedHas(t *testing.T) {
+	type Toy struct {
+		ID        int
+		Name      string
+		OwnerID   int
+		OwnerType string
+	}
+	type User struct {
+		ID  int
+		Cat struct {
+			Name string
+			Toy  Toy   `gorm:"polymorphic:Owner;"`
+			Toys []Toy `gorm:"polymorphic:Owner;"`
+		} `gorm:"embedded;embeddedPrefix:cat_"`
+		Dog struct {
+			ID     int
+			Name   string
+			UserID int
+			Toy    Toy   `gorm:"polymorphic:Owner;"`
+			Toys   []Toy `gorm:"polymorphic:Owner;"`
+		}
+		Toys []Toy `gorm:"polymorphic:Owner;"`
+	}
+
+	s, err := schema.Parse(&User{}, &sync.Map{}, schema.NamingStrategy{})
+	if err != nil {
+		t.Fatalf("Failed to parse schema, got error %v", err)
+	}
+
+	checkEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{
+		"Cat": {
+			Relations: map[string]Relation{
+				"Toy": {
+					Name:        "Toy",
+					Type:        schema.HasOne,
+					Schema:      "User",
+					FieldSchema: "Toy",
+					Polymorphic: Polymorphic{ID: "OwnerID", Type: "OwnerType", Value: "users"},
+					References: []Reference{
+						{ForeignKey: "OwnerType", ForeignSchema: "Toy", PrimaryValue: "users"},
+						{ForeignKey: "OwnerType", ForeignSchema: "Toy", PrimaryValue: "users"},
+					},
+				},
+				"Toys": {
+					Name:        "Toys",
+					Type:        schema.HasMany,
+					Schema:      "User",
+					FieldSchema: "Toy",
+					Polymorphic: Polymorphic{ID: "OwnerID", Type: "OwnerType", Value: "users"},
+					References: []Reference{
+						{ForeignKey: "OwnerType", ForeignSchema: "Toy", PrimaryValue: "users"},
+						{ForeignKey: "OwnerType", ForeignSchema: "Toy", PrimaryValue: "users"},
+					},
+				},
+			},
+		},
+	})
+}
+
+func TestEmbeddedBelongsTo(t *testing.T) {
+	type Country struct {
+		ID   int `gorm:"primaryKey"`
+		Name string
+	}
+	type Address struct {
+		CountryID int
+		Country   Country
+	}
+	type NestedAddress struct {
+		Address
+	}
+	type Org struct {
+		ID              int
+		PostalAddress   Address `gorm:"embedded;embeddedPrefix:postal_address_"`
+		VisitingAddress Address `gorm:"embedded;embeddedPrefix:visiting_address_"`
+		AddressID       int
+		Address         struct {
+			ID int
+			Address
+		}
+		NestedAddress *NestedAddress `gorm:"embedded;embeddedPrefix:nested_address_"`
+	}
+
+	s, err := schema.Parse(&Org{}, &sync.Map{}, schema.NamingStrategy{})
+	if err != nil {
+		t.Errorf("Failed to parse schema, got error %v", err)
+	}
+
+	checkEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{
+		"PostalAddress": {
+			Relations: map[string]Relation{
+				"Country": {
+					Name: "Country", Type: schema.BelongsTo, Schema: "Org", FieldSchema: "Country",
+					References: []Reference{
+						{PrimaryKey: "ID", PrimarySchema: "Country", ForeignKey: "CountryID", ForeignSchema: "Org"},
+					},
+				},
+			},
+		},
+		"VisitingAddress": {
+			Relations: map[string]Relation{
+				"Country": {
+					Name: "Country", Type: schema.BelongsTo, Schema: "Org", FieldSchema: "Country",
+					References: []Reference{
+						{PrimaryKey: "ID", PrimarySchema: "Country", ForeignKey: "CountryID", ForeignSchema: "Org"},
+					},
+				},
+			},
+		},
+		"NestedAddress": {
+			EmbeddedRelations: map[string]EmbeddedRelations{
+				"Address": {
+					Relations: map[string]Relation{
+						"Country": {
+							Name: "Country", Type: schema.BelongsTo, Schema: "Org", FieldSchema: "Country",
+							References: []Reference{
+								{PrimaryKey: "ID", PrimarySchema: "Country", ForeignKey: "CountryID", ForeignSchema: "Org"},
+							},
+						},
+					},
+				},
+			},
+		},
+	})
+}
+
 func TestVariableRelation(t *testing.T) {
 	var result struct {
 		User
diff --git a/schema/schema_helper_test.go b/schema/schema_helper_test.go
index 9abaecb..605aa03 100644
--- a/schema/schema_helper_test.go
+++ b/schema/schema_helper_test.go
@@ -201,6 +201,37 @@ func checkSchemaRelation(t *testing.T, s *schema.Schema, relation Relation) {
 	})
 }
 
+type EmbeddedRelations struct {
+	Relations         map[string]Relation
+	EmbeddedRelations map[string]EmbeddedRelations
+}
+
+func checkEmbeddedRelations(t *testing.T, actual map[string]*schema.Relationships, expected map[string]EmbeddedRelations) {
+	for name, relations := range actual {
+		rs := expected[name]
+		t.Run("CheckEmbeddedRelations/"+name, func(t *testing.T) {
+			if len(relations.Relations) != len(rs.Relations) {
+				t.Errorf("schema relations count don't match, expects %d, got %d", len(rs.Relations), len(relations.Relations))
+			}
+			if len(relations.EmbeddedRelations) != len(rs.EmbeddedRelations) {
+				t.Errorf("schema embedded relations count don't match, expects %d, got %d", len(rs.EmbeddedRelations), len(relations.EmbeddedRelations))
+			}
+			for n, rel := range relations.Relations {
+				if r, ok := rs.Relations[n]; !ok {
+					t.Errorf("failed to find relation by name %s", n)
+				} else {
+					checkSchemaRelation(t, &schema.Schema{
+						Relationships: schema.Relationships{
+							Relations: map[string]*schema.Relationship{n: rel},
+						},
+					}, r)
+				}
+			}
+			checkEmbeddedRelations(t, relations.EmbeddedRelations, rs.EmbeddedRelations)
+		})
+	}
+}
+
 func checkField(t *testing.T, s *schema.Schema, value reflect.Value, values map[string]interface{}) {
 	for k, v := range values {
 		t.Run("CheckField/"+k, func(t *testing.T) {
diff --git a/tests/associations_many2many_test.go b/tests/associations_many2many_test.go
index 845c16a..b69d668 100644
--- a/tests/associations_many2many_test.go
+++ b/tests/associations_many2many_test.go
@@ -393,3 +393,33 @@ func TestConcurrentMany2ManyAssociation(t *testing.T) {
 	AssertEqual(t, err, nil)
 	AssertAssociationCount(t, find, "Languages", int64(count), "after concurrent append")
 }
+
+func TestMany2ManyDuplicateBelongsToAssociation(t *testing.T) {
+	user1 := User{Name: "TestMany2ManyDuplicateBelongsToAssociation-1", Friends: []*User{
+		{Name: "TestMany2ManyDuplicateBelongsToAssociation-friend-1", Company: Company{
+			ID:   1,
+			Name: "Test-company-1",
+		}},
+	}}
+
+	user2 := User{Name: "TestMany2ManyDuplicateBelongsToAssociation-2", Friends: []*User{
+		{Name: "TestMany2ManyDuplicateBelongsToAssociation-friend-2", Company: Company{
+			ID:   1,
+			Name: "Test-company-1",
+		}},
+	}}
+	users := []*User{&user1, &user2}
+	var err error
+	err = DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(users).Error
+	AssertEqual(t, nil, err)
+
+	var findUser1 User
+	err = DB.Preload("Friends.Company").Where("id = ?", user1.ID).First(&findUser1).Error
+	AssertEqual(t, nil, err)
+	AssertEqual(t, user1, findUser1)
+
+	var findUser2 User
+	err = DB.Preload("Friends.Company").Where("id = ?", user2.ID).First(&findUser2).Error
+	AssertEqual(t, nil, err)
+	AssertEqual(t, user2, findUser2)
+}
diff --git a/tests/count_test.go b/tests/count_test.go
index 2199dc6..b0dfb0b 100644
--- a/tests/count_test.go
+++ b/tests/count_test.go
@@ -11,6 +11,32 @@ import (
 	. "gorm.io/gorm/utils/tests"
 )
 
+func TestCountWithGroup(t *testing.T) {
+	DB.Create([]Company{
+		{Name: "company_count_group_a"},
+		{Name: "company_count_group_a"},
+		{Name: "company_count_group_a"},
+		{Name: "company_count_group_b"},
+		{Name: "company_count_group_c"},
+	})
+
+	var count1 int64
+	if err := DB.Model(&Company{}).Where("name = ?", "company_count_group_a").Group("name").Count(&count1).Error; err != nil {
+		t.Errorf(fmt.Sprintf("Count should work, but got err %v", err))
+	}
+	if count1 != 1 {
+		t.Errorf("Count with group should be 1, but got count: %v", count1)
+	}
+
+	var count2 int64
+	if err := DB.Debug().Model(&Company{}).Where("name in ?", []string{"company_count_group_b", "company_count_group_c"}).Group("name").Count(&count2).Error; err != nil {
+		t.Errorf(fmt.Sprintf("Count should work, but got err %v", err))
+	}
+	if count2 != 2 {
+		t.Errorf("Count with group should be 2, but got count: %v", count2)
+	}
+}
+
 func TestCount(t *testing.T) {
 	var (
 		user1                 = *GetUser("count-1", Config{})
@@ -141,8 +167,8 @@ func TestCount(t *testing.T) {
 	}
 	DB.Create(sameUsers)
 
-	if err := DB.Model(&User{}).Where("name = ?", "count-4").Group("name").Count(&count11).Error; err != nil || count11 != int64(len(sameUsers)) {
-		t.Fatalf("Count should be 3, but got count: %v err %v", count11, err)
+	if err := DB.Model(&User{}).Where("name = ?", "count-4").Group("name").Count(&count11).Error; err != nil || count11 != 1 {
+		t.Fatalf("Count should be 1, but got count: %v err %v", count11, err)
 	}
 
 	var count12 int64
diff --git a/tests/embedded_struct_test.go b/tests/embedded_struct_test.go
index 63ec53e..0d240fd 100644
--- a/tests/embedded_struct_test.go
+++ b/tests/embedded_struct_test.go
@@ -103,9 +103,16 @@ func TestEmbeddedPointerTypeStruct(t *testing.T) {
 		URL   string
 	}
 
+	type Author struct {
+		ID    string
+		Name  string
+		Email string
+	}
+
 	type HNPost struct {
 		*BasePost
 		Upvotes int32
+		*Author `gorm:"EmbeddedPrefix:user_"` // Embedded struct
 	}
 
 	DB.Migrator().DropTable(&HNPost{})
@@ -123,6 +130,10 @@ func TestEmbeddedPointerTypeStruct(t *testing.T) {
 	if hnPost.Title != "embedded_pointer_type" {
 		t.Errorf("Should find correct value for embedded pointer type")
 	}
+
+	if hnPost.Author != nil {
+		t.Errorf("Expected to get back a nil Author but got: %v", hnPost.Author)
+	}
 }
 
 type Content struct {
diff --git a/tests/error_translator_test.go b/tests/error_translator_test.go
index 2e472e3..ead26fc 100644
--- a/tests/error_translator_test.go
+++ b/tests/error_translator_test.go
@@ -9,10 +9,20 @@ import (
 )
 
 func TestDialectorWithErrorTranslatorSupport(t *testing.T) {
+	// it shouldn't translate error when the TranslateError flag is false
 	translatedErr := errors.New("translated error")
+	untranslatedErr := errors.New("some random error")
 	db, _ := gorm.Open(tests.DummyDialector{TranslatedErr: translatedErr})
 
-	err := db.AddError(errors.New("some random error"))
+	err := db.AddError(untranslatedErr)
+	if errors.Is(err, translatedErr) {
+		t.Fatalf("expected err: %v got err: %v", translatedErr, err)
+	}
+
+	// it should translate error when the TranslateError flag is true
+	db, _ = gorm.Open(tests.DummyDialector{TranslatedErr: translatedErr}, &gorm.Config{TranslateError: true})
+
+	err = db.AddError(untranslatedErr)
 	if !errors.Is(err, translatedErr) {
 		t.Fatalf("expected err: %v got err: %v", translatedErr, err)
 	}
diff --git a/tests/preload_test.go b/tests/preload_test.go
index e7223b3..7304e35 100644
--- a/tests/preload_test.go
+++ b/tests/preload_test.go
@@ -306,3 +306,141 @@ func TestNestedPreloadWithUnscoped(t *testing.T) {
 	DB.Unscoped().Preload("Pets.Toy").Find(&user6, "id = ?", user.ID)
 	CheckUserUnscoped(t, *user6, user)
 }
+
+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",
+			preloads: map[string][]interface{}{"NestedAddress.EmbeddedAddress.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:       nil,
+				NestedAddress: org.NestedAddress,
+			},
+		}, {
+			name: "associations",
+			preloads: map[string][]interface{}{
+				clause.Associations: {},
+				// clause.Associations won’t preload nested associations
+				"Address.Country": {},
+			},
+			expect: org,
+		},
+	}
+
+	DB = DB.Debug()
+	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)
+		})
+	}
+}
diff --git a/tests/scopes_test.go b/tests/scopes_test.go
index ab3807e..52c6b37 100644
--- a/tests/scopes_test.go
+++ b/tests/scopes_test.go
@@ -72,3 +72,54 @@ func TestScopes(t *testing.T) {
 		t.Errorf("select max(id)")
 	}
 }
+
+func TestComplexScopes(t *testing.T) {
+	tests := []struct {
+		name     string
+		queryFn  func(tx *gorm.DB) *gorm.DB
+		expected string
+	}{
+		{
+			name: "depth_1",
+			queryFn: func(tx *gorm.DB) *gorm.DB {
+				return tx.Scopes(
+					func(d *gorm.DB) *gorm.DB { return d.Where("a = 1") },
+					func(d *gorm.DB) *gorm.DB { return d.Where(d.Or("b = 2").Or("c = 3")) },
+				).Find(&Language{})
+			},
+			expected: `SELECT * FROM "languages" WHERE a = 1 AND (b = 2 OR c = 3)`,
+		}, {
+			name: "depth_1_pre_cond",
+			queryFn: func(tx *gorm.DB) *gorm.DB {
+				return tx.Where("z = 0").Scopes(
+					func(d *gorm.DB) *gorm.DB { return d.Where("a = 1") },
+					func(d *gorm.DB) *gorm.DB { return d.Or(d.Where("b = 2").Or("c = 3")) },
+				).Find(&Language{})
+			},
+			expected: `SELECT * FROM "languages" WHERE z = 0 AND a = 1 OR (b = 2 OR c = 3)`,
+		}, {
+			name: "depth_2",
+			queryFn: func(tx *gorm.DB) *gorm.DB {
+				return tx.Scopes(
+					func(d *gorm.DB) *gorm.DB { return d.Model(&Language{}) },
+					func(d *gorm.DB) *gorm.DB {
+						return d.
+							Or(d.Scopes(
+								func(d *gorm.DB) *gorm.DB { return d.Where("a = 1") },
+								func(d *gorm.DB) *gorm.DB { return d.Where("b = 2") },
+							)).
+							Or("c = 3")
+					},
+					func(d *gorm.DB) *gorm.DB { return d.Where("d = 4") },
+				).Find(&Language{})
+			},
+			expected: `SELECT * FROM "languages" WHERE d = 4 OR c = 3 OR (a = 1 AND b = 2)`,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			assertEqualSQL(t, test.expected, DB.ToSQL(test.queryFn))
+		})
+	}
+}
diff --git a/tests/update_test.go b/tests/update_test.go
index b2da11c..36ffa6a 100644
--- a/tests/update_test.go
+++ b/tests/update_test.go
@@ -610,6 +610,25 @@ func TestUpdateFromSubQuery(t *testing.T) {
 	}
 }
 
+func TestIdempotentSave(t *testing.T) {
+	create := Company{
+		Name: "company_idempotent",
+	}
+	DB.Create(&create)
+
+	var company Company
+	if err := DB.Find(&company, "id = ?", create.ID).Error; err != nil {
+		t.Fatalf("failed to find created company, got err: %v", err)
+	}
+
+	if err := DB.Save(&company).Error; err != nil || company.ID != create.ID {
+		t.Errorf("failed to save company, got err: %v", err)
+	}
+	if err := DB.Save(&company).Error; err != nil || company.ID != create.ID {
+		t.Errorf("failed to save company, got err: %v", err)
+	}
+}
+
 func TestSave(t *testing.T) {
 	user := *GetUser("save", Config{})
 	DB.Create(&user)