gorm/callbacks/update.go

248 lines
7.3 KiB
Go
Raw Normal View History

2020-02-02 14:32:27 +03:00
package callbacks
2020-02-23 16:22:35 +03:00
import (
"reflect"
2020-03-07 08:43:20 +03:00
"sort"
2020-02-23 16:22:35 +03:00
2020-06-02 04:16:07 +03:00
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
2020-02-23 16:22:35 +03:00
)
2020-02-02 14:32:27 +03:00
2020-06-01 14:41:33 +03:00
func SetupUpdateReflectValue(db *gorm.DB) {
2020-06-05 14:18:22 +03:00
if db.Error == nil && db.Statement.Schema != nil {
2020-06-01 14:41:33 +03:00
if !db.Statement.ReflectValue.CanAddr() || db.Statement.Model != db.Statement.Dest {
db.Statement.ReflectValue = reflect.ValueOf(db.Statement.Model)
for db.Statement.ReflectValue.Kind() == reflect.Ptr {
db.Statement.ReflectValue = db.Statement.ReflectValue.Elem()
}
if dest, ok := db.Statement.Dest.(map[string]interface{}); ok {
for _, rel := range db.Statement.Schema.Relationships.BelongsTo {
if _, ok := dest[rel.Name]; ok {
rel.Field.Set(db.Statement.ReflectValue, dest[rel.Name])
}
}
}
}
}
}
2020-02-02 14:32:27 +03:00
func BeforeUpdate(db *gorm.DB) {
2020-06-05 15:24:15 +03:00
if db.Error == nil && db.Statement.Schema != nil && !db.Statement.UpdatingColumn && (db.Statement.Schema.BeforeSave || db.Statement.Schema.BeforeUpdate) {
callMethod(db, func(value interface{}, tx *gorm.DB) (called bool) {
2020-02-23 16:22:35 +03:00
if db.Statement.Schema.BeforeSave {
if i, ok := value.(gorm.BeforeSaveInterface); ok {
2020-06-05 15:24:15 +03:00
called = true
2020-05-31 18:55:56 +03:00
db.AddError(i.BeforeSave(tx))
2020-02-23 16:22:35 +03:00
}
}
if db.Statement.Schema.BeforeUpdate {
if i, ok := value.(gorm.BeforeUpdateInterface); ok {
2020-06-05 15:24:15 +03:00
called = true
2020-05-31 18:55:56 +03:00
db.AddError(i.BeforeUpdate(tx))
2020-02-23 16:22:35 +03:00
}
}
return called
})
2020-02-23 16:22:35 +03:00
}
2020-02-02 14:32:27 +03:00
}
func Update(db *gorm.DB) {
2020-05-31 18:55:56 +03:00
if db.Error == nil {
if db.Statement.Schema != nil && !db.Statement.Unscoped {
for _, c := range db.Statement.Schema.UpdateClauses {
db.Statement.AddClause(c)
}
2020-05-29 02:35:45 +03:00
}
2020-05-31 18:55:56 +03:00
if db.Statement.SQL.String() == "" {
db.Statement.AddClauseIfNotExists(clause.Update{})
if set := ConvertToAssignments(db.Statement); len(set) != 0 {
db.Statement.AddClause(set)
} else {
return
}
db.Statement.Build("UPDATE", "SET", "WHERE")
2020-05-29 02:35:45 +03:00
}
2020-03-07 08:43:20 +03:00
2020-05-31 18:55:56 +03:00
if _, ok := db.Statement.Clauses["WHERE"]; !ok {
db.AddError(gorm.ErrMissingWhereClause)
return
}
2020-05-31 15:42:07 +03:00
2020-06-01 16:26:23 +03:00
if !db.DryRun {
result, err := db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
2020-03-07 08:43:20 +03:00
2020-06-01 16:26:23 +03:00
if err == nil {
db.RowsAffected, _ = result.RowsAffected()
} else {
db.AddError(err)
}
2020-05-31 18:55:56 +03:00
}
2020-03-07 08:43:20 +03:00
}
2020-02-02 14:32:27 +03:00
}
func AfterUpdate(db *gorm.DB) {
2020-06-05 15:24:15 +03:00
if db.Error == nil && db.Statement.Schema != nil && !db.Statement.UpdatingColumn && (db.Statement.Schema.AfterSave || db.Statement.Schema.AfterUpdate) {
callMethod(db, func(value interface{}, tx *gorm.DB) (called bool) {
2020-02-23 16:22:35 +03:00
if db.Statement.Schema.AfterSave {
if i, ok := value.(gorm.AfterSaveInterface); ok {
2020-06-05 15:24:15 +03:00
called = true
2020-05-31 18:55:56 +03:00
db.AddError(i.AfterSave(tx))
2020-02-23 16:22:35 +03:00
}
}
if db.Statement.Schema.AfterUpdate {
if i, ok := value.(gorm.AfterUpdateInterface); ok {
2020-06-05 15:24:15 +03:00
called = true
2020-05-31 18:55:56 +03:00
db.AddError(i.AfterUpdate(tx))
2020-02-23 16:22:35 +03:00
}
}
2020-06-05 15:24:15 +03:00
return called
})
2020-02-23 16:22:35 +03:00
}
2020-02-02 14:32:27 +03:00
}
2020-03-07 08:43:20 +03:00
// ConvertToAssignments convert to update assignments
2020-03-08 08:24:08 +03:00
func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) {
2020-05-24 12:24:23 +03:00
var (
selectColumns, restricted = SelectAndOmitColumns(stmt, false, true)
assignValue func(field *schema.Field, value interface{})
)
2020-06-01 14:41:33 +03:00
switch stmt.ReflectValue.Kind() {
2020-05-24 12:24:23 +03:00
case reflect.Slice, reflect.Array:
assignValue = func(field *schema.Field, value interface{}) {
2020-06-01 14:41:33 +03:00
for i := 0; i < stmt.ReflectValue.Len(); i++ {
field.Set(stmt.ReflectValue.Index(i), value)
2020-05-24 12:24:23 +03:00
}
}
case reflect.Struct:
assignValue = func(field *schema.Field, value interface{}) {
2020-06-01 14:41:33 +03:00
if stmt.ReflectValue.CanAddr() {
field.Set(stmt.ReflectValue, value)
}
2020-05-24 12:24:23 +03:00
}
default:
assignValue = func(field *schema.Field, value interface{}) {
}
}
2020-03-07 08:43:20 +03:00
2020-06-01 14:41:33 +03:00
updatingValue := reflect.ValueOf(stmt.Dest)
for updatingValue.Kind() == reflect.Ptr {
updatingValue = updatingValue.Elem()
}
switch value := updatingValue.Interface().(type) {
2020-03-07 08:43:20 +03:00
case map[string]interface{}:
2020-03-08 08:24:08 +03:00
set = make([]clause.Assignment, 0, len(value))
2020-03-07 08:43:20 +03:00
var keys []string
2020-06-07 10:24:34 +03:00
for k := range value {
2020-03-07 08:43:20 +03:00
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
2020-06-05 14:18:22 +03:00
if stmt.Schema != nil {
if field := stmt.Schema.LookUpField(k); field != nil {
if field.DBName != "" {
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {
set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: value[k]})
assignValue(field, value[k])
}
} else if v, ok := selectColumns[field.Name]; (ok && v) || (!ok && !restricted) {
2020-06-01 14:41:33 +03:00
assignValue(field, value[k])
}
2020-06-05 14:18:22 +03:00
continue
2020-03-07 08:43:20 +03:00
}
2020-06-05 14:18:22 +03:00
}
if v, ok := selectColumns[k]; (ok && v) || (!ok && !restricted) {
2020-03-07 08:43:20 +03:00
set = append(set, clause.Assignment{Column: clause.Column{Name: k}, Value: value[k]})
}
}
2020-06-05 15:24:15 +03:00
if !stmt.UpdatingColumn && stmt.Schema != nil {
2020-05-30 12:34:22 +03:00
for _, field := range stmt.Schema.FieldsByDBName {
if field.AutoUpdateTime > 0 && value[field.Name] == nil && value[field.DBName] == nil {
2020-05-31 02:57:13 +03:00
now := stmt.DB.NowFunc()
2020-05-30 12:34:22 +03:00
assignValue(field, now)
2020-06-05 14:18:22 +03:00
if field.AutoUpdateTime == schema.UnixNanosecond {
set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now.UnixNano()})
} else if field.DataType == schema.Time {
set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now})
} else {
set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now.Unix()})
}
2020-05-30 12:34:22 +03:00
}
2020-03-08 08:24:08 +03:00
}
}
2020-03-07 08:43:20 +03:00
default:
2020-06-01 14:41:33 +03:00
switch updatingValue.Kind() {
2020-03-07 08:43:20 +03:00
case reflect.Struct:
2020-03-08 08:24:08 +03:00
set = make([]clause.Assignment, 0, len(stmt.Schema.FieldsByDBName))
2020-03-07 08:43:20 +03:00
for _, field := range stmt.Schema.FieldsByDBName {
2020-06-01 14:41:33 +03:00
if !field.PrimaryKey || (!updatingValue.CanAddr() || stmt.Dest != stmt.Model) {
2020-03-08 08:24:08 +03:00
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {
2020-06-01 14:41:33 +03:00
value, isZero := field.ValueOf(updatingValue)
2020-06-05 15:24:15 +03:00
if !stmt.UpdatingColumn {
2020-05-30 12:34:22 +03:00
if field.AutoUpdateTime > 0 {
2020-06-05 14:18:22 +03:00
if field.AutoUpdateTime == schema.UnixNanosecond {
value = stmt.DB.NowFunc().UnixNano()
} else if field.DataType == schema.Time {
value = stmt.DB.NowFunc()
} else {
value = stmt.DB.NowFunc().Unix()
}
2020-05-30 12:34:22 +03:00
isZero = false
}
2020-03-08 08:24:08 +03:00
}
if ok || !isZero {
set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: value})
2020-05-24 12:24:23 +03:00
assignValue(field, value)
2020-03-08 08:24:08 +03:00
}
}
} else {
2020-06-01 14:41:33 +03:00
if value, isZero := field.ValueOf(updatingValue); !isZero {
2020-03-08 08:24:08 +03:00
stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Eq{Column: field.DBName, Value: value}}})
}
2020-03-07 08:43:20 +03:00
}
}
}
}
2020-06-01 14:41:33 +03:00
if !updatingValue.CanAddr() || stmt.Dest != stmt.Model {
switch stmt.ReflectValue.Kind() {
2020-05-24 15:44:37 +03:00
case reflect.Slice, reflect.Array:
var priamryKeyExprs []clause.Expression
2020-06-01 14:41:33 +03:00
for i := 0; i < stmt.ReflectValue.Len(); i++ {
2020-05-24 15:44:37 +03:00
var exprs = make([]clause.Expression, len(stmt.Schema.PrimaryFields))
var notZero bool
for idx, field := range stmt.Schema.PrimaryFields {
2020-06-01 14:41:33 +03:00
value, isZero := field.ValueOf(stmt.ReflectValue.Index(i))
2020-05-24 15:44:37 +03:00
exprs[idx] = clause.Eq{Column: field.DBName, Value: value}
notZero = notZero || !isZero
}
if notZero {
priamryKeyExprs = append(priamryKeyExprs, clause.And(exprs...))
}
}
stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Or(priamryKeyExprs...)}})
case reflect.Struct:
for _, field := range stmt.Schema.PrimaryFields {
2020-06-01 14:41:33 +03:00
if value, isZero := field.ValueOf(stmt.ReflectValue); !isZero {
2020-05-24 15:44:37 +03:00
stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Eq{Column: field.DBName, Value: value}}})
}
2020-03-08 08:24:08 +03:00
}
}
}
2020-05-24 17:52:16 +03:00
2020-03-08 08:24:08 +03:00
return
2020-03-07 08:43:20 +03:00
}