From 397b583b8ecc5a31c838db5822fe1003b53a91ef Mon Sep 17 00:00:00 2001 From: chenrui Date: Fri, 25 Feb 2022 22:38:48 +0800 Subject: [PATCH 1/3] fix: query scanner in single column --- scan.go | 12 +++++++++++- tests/query_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/scan.go b/scan.go index 0da12daf..a1cb582e 100644 --- a/scan.go +++ b/scan.go @@ -272,7 +272,17 @@ func Scan(rows *sql.Rows, db *DB, mode ScanMode) { } case reflect.Struct, reflect.Ptr: if initialized || rows.Next() { - db.scanIntoStruct(sch, rows, reflectValue, values, columns, fields, joinFields) + if update { + db.scanIntoStruct(sch, rows, reflectValue, values, columns, fields, joinFields) + } else { + elem := reflect.New(reflectValueType) + db.scanIntoStruct(sch, rows, elem, values, columns, fields, joinFields) + if isPtr { + db.Statement.ReflectValue.Set(elem) + } else { + db.Statement.ReflectValue.Set(elem.Elem()) + } + } } default: db.AddError(rows.Scan(dest)) diff --git a/tests/query_test.go b/tests/query_test.go index d10df180..6542774a 100644 --- a/tests/query_test.go +++ b/tests/query_test.go @@ -1158,3 +1158,39 @@ func TestQueryWithTableAndConditionsAndAllFields(t *testing.T) { t.Errorf("invalid query SQL, got %v", result.Statement.SQL.String()) } } + +type DoubleInt64 struct { + data int64 +} + +func (t *DoubleInt64) Scan(val interface{}) error { + switch v := val.(type) { + case int64: + t.data = v * 2 + return nil + default: + return fmt.Errorf("DoubleInt64 cant not scan with:%v", v) + } +} + +// https://github.com/go-gorm/gorm/issues/5091 +func TestQueryScannerWithSingleColumn(t *testing.T) { + user := User{Name: "scanner_raw_1", Age: 10} + DB.Create(&user) + + var result1 DoubleInt64 + if err := DB.Model(&User{}).Where("name LIKE ?", "scanner_raw_%").Limit(1).Pluck( + "age", &result1).Error; err != nil { + t.Errorf("Failed, got error: %v", err) + } + + AssertEqual(t, result1.data, 20) + + var result2 DoubleInt64 + if err := DB.Model(&User{}).Where("name LIKE ?", "scanner_raw_%").Limit(1).Select( + "age").Scan(&result2).Error; err != nil { + t.Errorf("Failed, got error: %v", err) + } + + AssertEqual(t, result2.data, 20) +} From f2edda50e11728e7aee6b1d4c961d575f7afbb2d Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Fri, 25 Feb 2022 10:48:23 +0800 Subject: [PATCH 2/3] Refactor check missing where condition --- callbacks/delete.go | 19 +++++++------------ callbacks/helper.go | 16 ++++++++++++++++ callbacks/update.go | 20 +++++++------------- soft_delete.go | 11 ++--------- tests/update_test.go | 2 +- 5 files changed, 33 insertions(+), 35 deletions(-) diff --git a/callbacks/delete.go b/callbacks/delete.go index 1fb5261c..84f446a3 100644 --- a/callbacks/delete.go +++ b/callbacks/delete.go @@ -118,6 +118,12 @@ func Delete(config *Config) func(db *gorm.DB) { return } + if db.Statement.Schema != nil { + for _, c := range db.Statement.Schema.DeleteClauses { + db.Statement.AddClause(c) + } + } + if db.Statement.SQL.Len() == 0 { db.Statement.SQL.Grow(100) db.Statement.AddClauseIfNotExists(clause.Delete{}) @@ -141,22 +147,11 @@ func Delete(config *Config) func(db *gorm.DB) { } db.Statement.AddClauseIfNotExists(clause.From{}) - } - if db.Statement.Schema != nil { - for _, c := range db.Statement.Schema.DeleteClauses { - db.Statement.AddClause(c) - } - } - - if db.Statement.SQL.Len() == 0 { db.Statement.Build(db.Statement.BuildClauses...) } - if _, ok := db.Statement.Clauses["WHERE"]; !db.AllowGlobalUpdate && !ok && db.Error == nil { - db.AddError(gorm.ErrMissingWhereClause) - return - } + checkMissingWhereConditions(db) if !db.DryRun && db.Error == nil { ok, mode := hasReturning(db, supportReturning) diff --git a/callbacks/helper.go b/callbacks/helper.go index a59e1880..a5eb047e 100644 --- a/callbacks/helper.go +++ b/callbacks/helper.go @@ -104,3 +104,19 @@ func hasReturning(tx *gorm.DB, supportReturning bool) (bool, gorm.ScanMode) { } return false, 0 } + +func checkMissingWhereConditions(db *gorm.DB) { + if !db.AllowGlobalUpdate && db.Error == nil { + where, withCondition := db.Statement.Clauses["WHERE"] + if withCondition { + if _, withSoftDelete := db.Statement.Clauses["soft_delete_enabled"]; withSoftDelete { + whereClause, _ := where.Expression.(clause.Where) + withCondition = len(whereClause.Exprs) > 1 + } + } + if !withCondition { + db.AddError(gorm.ErrMissingWhereClause) + } + return + } +} diff --git a/callbacks/update.go b/callbacks/update.go index 4a2e5c79..da03261e 100644 --- a/callbacks/update.go +++ b/callbacks/update.go @@ -59,6 +59,12 @@ func Update(config *Config) func(db *gorm.DB) { return } + if db.Statement.Schema != nil { + for _, c := range db.Statement.Schema.UpdateClauses { + db.Statement.AddClause(c) + } + } + if db.Statement.SQL.Len() == 0 { db.Statement.SQL.Grow(180) db.Statement.AddClauseIfNotExists(clause.Update{}) @@ -68,22 +74,10 @@ func Update(config *Config) func(db *gorm.DB) { return } - } - - if db.Statement.Schema != nil { - for _, c := range db.Statement.Schema.UpdateClauses { - db.Statement.AddClause(c) - } - } - - if db.Statement.SQL.Len() == 0 { db.Statement.Build(db.Statement.BuildClauses...) } - if _, ok := db.Statement.Clauses["WHERE"]; !db.AllowGlobalUpdate && !ok { - db.AddError(gorm.ErrMissingWhereClause) - return - } + checkMissingWhereConditions(db) if !db.DryRun && db.Error == nil { if ok, mode := hasReturning(db, supportReturning); ok { diff --git a/soft_delete.go b/soft_delete.go index ba6d2118..6d646288 100644 --- a/soft_delete.go +++ b/soft_delete.go @@ -104,9 +104,7 @@ func (sd SoftDeleteUpdateClause) MergeClause(*clause.Clause) { func (sd SoftDeleteUpdateClause) ModifyStatement(stmt *Statement) { if stmt.SQL.Len() == 0 && !stmt.Statement.Unscoped { - if _, ok := stmt.Clauses["WHERE"]; stmt.DB.AllowGlobalUpdate || ok { - SoftDeleteQueryClause(sd).ModifyStatement(stmt) - } + SoftDeleteQueryClause(sd).ModifyStatement(stmt) } } @@ -152,12 +150,7 @@ func (sd SoftDeleteDeleteClause) ModifyStatement(stmt *Statement) { } } - if _, ok := stmt.Clauses["WHERE"]; !stmt.DB.AllowGlobalUpdate && !ok { - stmt.DB.AddError(ErrMissingWhereClause) - } else { - SoftDeleteQueryClause(sd).ModifyStatement(stmt) - } - + SoftDeleteQueryClause(sd).ModifyStatement(stmt) stmt.AddClauseIfNotExists(clause.Update{}) stmt.Build(stmt.DB.Callback().Update().Clauses...) } diff --git a/tests/update_test.go b/tests/update_test.go index b471ba9b..41ea5d27 100644 --- a/tests/update_test.go +++ b/tests/update_test.go @@ -645,7 +645,7 @@ func TestSave(t *testing.T) { dryDB := DB.Session(&gorm.Session{DryRun: true}) stmt := dryDB.Save(&user).Statement - if !regexp.MustCompile(`.id. = .* AND .users.\..deleted_at. IS NULL`).MatchString(stmt.SQL.String()) { + if !regexp.MustCompile(`.users.\..deleted_at. IS NULL`).MatchString(stmt.SQL.String()) { t.Fatalf("invalid updating SQL, got %v", stmt.SQL.String()) } From 68bb5379d91a7f7fae4dc65205db66004f515d0c Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Sun, 27 Feb 2022 09:09:29 +0800 Subject: [PATCH 3/3] Refactor scan into struct --- scan.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/scan.go b/scan.go index a1cb582e..e83390ca 100644 --- a/scan.go +++ b/scan.go @@ -68,7 +68,11 @@ func (db *DB) scanIntoStruct(sch *schema.Schema, rows *sql.Rows, reflectValue re values[idx] = &sql.RawBytes{} } else if len(columns) == 1 { sch = nil - values[idx] = reflectValue.Interface() + if reflectValue.CanAddr() { + values[idx] = reflectValue.Addr().Interface() + } else { + values[idx] = reflectValue.Interface() + } } else { values[idx] = &sql.RawBytes{} } @@ -272,17 +276,7 @@ func Scan(rows *sql.Rows, db *DB, mode ScanMode) { } case reflect.Struct, reflect.Ptr: if initialized || rows.Next() { - if update { - db.scanIntoStruct(sch, rows, reflectValue, values, columns, fields, joinFields) - } else { - elem := reflect.New(reflectValueType) - db.scanIntoStruct(sch, rows, elem, values, columns, fields, joinFields) - if isPtr { - db.Statement.ReflectValue.Set(elem) - } else { - db.Statement.ReflectValue.Set(elem.Elem()) - } - } + db.scanIntoStruct(sch, rows, reflectValue, values, columns, fields, joinFields) } default: db.AddError(rows.Scan(dest))