Repository: go-gorm/gorm
Pull request #6152 (2023-03-15 09:11:43)

fix cond in scopes
<!--
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?

fix conditions in `Scope` #6148 

### User Case Description

```go
DB.Table("test").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"))
	},
).Rows()
```
It should be:
```sql
SELECT * FROM `test` WHERE a = 1 AND (b = 2 OR c = 3)
```
But now it is:
```sql
SELECT * FROM `test` WHERE a = 1 OR b = 2 OR c = 3 AND (a = 1 OR b = 2 OR c = 3)
```



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/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..15edab9 100644
--- a/schema/field.go
+++ b/schema/field.go
@@ -580,8 +580,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 +599,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 +663,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 +715,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 +759,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/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/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/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)