Repository: go-gorm/gorm
Pull request #5023 (2022-01-24 07:42:55)

preoload not allowd before count
https://github.com/go-gorm/gorm/pull/4833#issuecomment-961566529


Patch:
diff --git a/callbacks/query.go b/callbacks/query.go
index c2bbf5f..4908635 100644
--- a/callbacks/query.go
+++ b/callbacks/query.go
@@ -100,7 +100,7 @@ func BuildQuerySQL(db *gorm.DB) {
 		}
 
 		if len(db.Statement.Joins) != 0 || len(joins) != 0 {
-			if len(db.Statement.Selects) == 0 && db.Statement.Schema != nil {
+			if len(db.Statement.Selects) == 0 && len(db.Statement.Omits) == 0 && db.Statement.Schema != nil {
 				clauseSelect.Columns = make([]clause.Column, len(db.Statement.Schema.DBNames))
 				for idx, dbName := range db.Statement.Schema.DBNames {
 					clauseSelect.Columns[idx] = clause.Column{Table: db.Statement.Table, Name: dbName}
diff --git a/errors.go b/errors.go
index 145614d..49cbfe6 100644
--- a/errors.go
+++ b/errors.go
@@ -39,4 +39,6 @@ var (
 	ErrInvalidValue = errors.New("invalid value, should be pointer to struct or slice")
 	// ErrInvalidValueOfLength invalid values do not match length
 	ErrInvalidValueOfLength = errors.New("invalid association values, length doesn't match")
+	// ErrPreloadNotAllowed preload is not allowed when count is used
+	ErrPreloadNotAllowed = errors.New("preload is not allowed when count is used")
 )
diff --git a/finisher_api.go b/finisher_api.go
index 355d89b..cbbd48c 100644
--- a/finisher_api.go
+++ b/finisher_api.go
@@ -367,6 +367,10 @@ func (db *DB) Delete(value interface{}, conds ...interface{}) (tx *DB) {
 
 func (db *DB) Count(count *int64) (tx *DB) {
 	tx = db.getInstance()
+	if len(tx.Statement.Preloads) > 0 {
+		tx.AddError(ErrPreloadNotAllowed)
+		return
+	}
 	if tx.Statement.Model == nil {
 		tx.Statement.Model = tx.Statement.Dest
 		defer func() {
diff --git a/gorm.go b/gorm.go
index fc70f68..a982bee 100644
--- a/gorm.go
+++ b/gorm.go
@@ -96,6 +96,7 @@ type Session struct {
 	DryRun                   bool
 	PrepareStmt              bool
 	NewDB                    bool
+	Initialized              bool
 	SkipHooks                bool
 	SkipDefaultTransaction   bool
 	DisableNestedTransaction bool
@@ -282,6 +283,10 @@ func (db *DB) Session(config *Session) *DB {
 		tx.Config.NowFunc = config.NowFunc
 	}
 
+	if config.Initialized {
+		tx = tx.getInstance()
+	}
+
 	return tx
 }
 
diff --git a/statement.go b/statement.go
index 146722a..72359da 100644
--- a/statement.go
+++ b/statement.go
@@ -183,6 +183,8 @@ func (stmt *Statement) AddVar(writer clause.Writer, vars ...interface{}) {
 			v.Build(stmt)
 		case *clause.Expr:
 			v.Build(stmt)
+		case clause.Expression:
+			v.Build(stmt)
 		case driver.Valuer:
 			stmt.Vars = append(stmt.Vars, v)
 			stmt.DB.Dialector.BindVarTo(writer, stmt, v)


Test patch:
diff --git a/clause/select_test.go b/clause/select_test.go
index 9fce078..18bc269 100644
--- a/clause/select_test.go
+++ b/clause/select_test.go
@@ -43,6 +43,23 @@ func TestSelect(t *testing.T) {
 			}, clause.From{}},
 			"SELECT `id`, `name`, LENGTH(`mobile`) FROM `users`", nil,
 		},
+		{
+			[]clause.Interface{clause.Select{
+				Expression: clause.CommaExpression{
+					Exprs: []clause.Expression{
+						clause.Expr{
+							SQL: "? as name",
+							Vars: []interface{}{clause.Eq{
+								Column: clause.Column{Name: "age"},
+								Value:  18,
+							},
+							},
+						},
+					},
+				},
+			}, clause.From{}},
+			"SELECT `age` = ? as name FROM `users`", []interface{}{18},
+		},
 	}
 
 	for idx, result := range results {
diff --git a/tests/connection_test.go b/tests/connection_test.go
index 92b13dd..7bc2300 100644
--- a/tests/connection_test.go
+++ b/tests/connection_test.go
@@ -9,7 +9,7 @@ import (
 )
 
 func TestWithSingleConnection(t *testing.T) {
-	var expectedName = "test"
+	expectedName := "test"
 	var actualName string
 
 	setSQL, getSQL := getSetSQL(DB.Dialector.Name())
@@ -27,7 +27,6 @@ func TestWithSingleConnection(t *testing.T) {
 		}
 		return nil
 	})
-
 	if err != nil {
 		t.Errorf(fmt.Sprintf("WithSingleConnection should work, but got err %v", err))
 	}
diff --git a/tests/count_test.go b/tests/count_test.go
index 27d7ee6..b63a55f 100644
--- a/tests/count_test.go
+++ b/tests/count_test.go
@@ -144,4 +144,14 @@ func TestCount(t *testing.T) {
 	if err := DB.Model(&User{}).Where("name = ?", "count-4").Group("name").Count(&count11).Error; err != nil || count11 != 1 {
 		t.Fatalf("Count should be 3, but got count: %v err %v", count11, err)
 	}
+
+	var count12 int64
+	if err := DB.Table("users").
+		Where("name in ?", []string{user1.Name, user2.Name, user3.Name}).
+		Preload("Toys", func(db *gorm.DB) *gorm.DB {
+			return db.Table("toys").Select("name")
+		}).Count(&count12).Error; err != gorm.ErrPreloadNotAllowed {
+		t.Errorf("should returns preload not allowed error, but got %v", err)
+	}
+	
 }
diff --git a/tests/go.mod b/tests/go.mod
index 3233ea9..5415cf7 100644
--- a/tests/go.mod
+++ b/tests/go.mod
@@ -3,11 +3,13 @@ module gorm.io/gorm/tests
 go 1.14
 
 require (
+	github.com/denisenkom/go-mssqldb v0.12.0 // indirect
 	github.com/google/uuid v1.3.0
 	github.com/jackc/pgx/v4 v4.14.1 // indirect
 	github.com/jinzhu/now v1.1.4
 	github.com/lib/pq v1.10.4
-	golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b // indirect
+	github.com/mattn/go-sqlite3 v1.14.10 // indirect
+	golang.org/x/crypto v0.0.0-20220126234351-aa10faf2a1f8 // indirect
 	gorm.io/driver/mysql v1.2.3
 	gorm.io/driver/postgres v1.2.3
 	gorm.io/driver/sqlite v1.2.6
diff --git a/tests/joins_test.go b/tests/joins_test.go
index e276a74..4c9cffa 100644
--- a/tests/joins_test.go
+++ b/tests/joins_test.go
@@ -158,6 +158,22 @@ func TestJoinsWithSelect(t *testing.T) {
 	}
 }
 
+func TestJoinWithOmit(t *testing.T) {
+	user := *GetUser("joins_with_omit", Config{Pets: 2})
+	DB.Save(&user)
+
+	results := make([]*User, 0)
+
+	if err := DB.Table("users").Omit("name").Where("users.name = ?", "joins_with_omit").Joins("left join pets on pets.user_id = users.id").Find(&results).Error; err != nil {
+		return
+	}
+
+	if len(results) != 2 || results[0].Name != "" || results[1].Name != "" {
+		t.Errorf("Should find all two pets with Join omit and should not find user's name, got %+v", results)
+		return
+	}
+}
+
 func TestJoinCount(t *testing.T) {
 	companyA := Company{Name: "A"}
 	companyB := Company{Name: "B"}