2017-01-03 05:31:47 +00:00
|
|
|
// Copyright 2016 The Xorm Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package xorm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
|
2019-06-17 05:38:13 +00:00
|
|
|
"xorm.io/builder"
|
2023-10-27 14:27:46 +00:00
|
|
|
"xorm.io/xorm/v2/internal/statements"
|
|
|
|
"xorm.io/xorm/v2/internal/utils"
|
|
|
|
"xorm.io/xorm/v2/schemas"
|
2017-01-03 05:31:47 +00:00
|
|
|
)
|
|
|
|
|
2021-06-12 03:43:45 +00:00
|
|
|
// enumerated all errors
|
|
|
|
var (
|
2023-07-25 10:49:55 +00:00
|
|
|
ErrNoColumnsTobeUpdated = statements.ErrNoColumnsTobeUpdated
|
2021-06-12 03:43:45 +00:00
|
|
|
)
|
|
|
|
|
2023-07-20 14:45:31 +00:00
|
|
|
func (session *Session) genAutoCond(condiBean interface{}) (builder.Cond, error) {
|
|
|
|
if session.statement.NoAutoCondition {
|
|
|
|
return builder.NewCond(), nil
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
|
2023-07-20 14:45:31 +00:00
|
|
|
if c, ok := condiBean.(map[string]interface{}); ok {
|
|
|
|
eq := make(builder.Eq)
|
|
|
|
for k, v := range c {
|
|
|
|
eq[session.engine.Quote(k)] = v
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
|
2023-07-20 14:45:31 +00:00
|
|
|
if session.statement.RefTable != nil {
|
|
|
|
if col := session.statement.RefTable.DeletedColumn(); col != nil && !session.statement.GetUnscoped() { // tag "deleted" is enabled
|
|
|
|
return eq.And(session.statement.CondDeleted(col)), nil
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
2021-07-20 05:46:24 +00:00
|
|
|
}
|
2023-07-20 14:45:31 +00:00
|
|
|
return eq, nil
|
|
|
|
}
|
2017-01-03 05:31:47 +00:00
|
|
|
|
2023-07-20 14:45:31 +00:00
|
|
|
ct := reflect.TypeOf(condiBean)
|
|
|
|
k := ct.Kind()
|
|
|
|
if k == reflect.Ptr {
|
|
|
|
k = ct.Elem().Kind()
|
|
|
|
}
|
|
|
|
if k != reflect.Struct {
|
|
|
|
return nil, ErrConditionType
|
|
|
|
}
|
2017-01-03 05:31:47 +00:00
|
|
|
|
2023-07-20 14:45:31 +00:00
|
|
|
condTable, err := session.engine.TableInfo(condiBean)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
2023-07-20 14:45:31 +00:00
|
|
|
return session.statement.BuildConds(condTable, condiBean, true, true, false, true, false)
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update records, bean's non-empty fields are updated contents,
|
|
|
|
// condiBean' non-empty filds are conditions
|
|
|
|
// CAUTION:
|
2023-02-12 03:16:53 +00:00
|
|
|
//
|
|
|
|
// 1.bool will defaultly be updated content nor conditions
|
|
|
|
// You should call UseBool if you have bool to use.
|
|
|
|
// 2.float32 & float64 may be not inexact as conditions
|
2017-01-03 05:31:47 +00:00
|
|
|
func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int64, error) {
|
2017-07-27 05:32:35 +00:00
|
|
|
if session.isAutoClose {
|
2017-01-03 05:31:47 +00:00
|
|
|
defer session.Close()
|
|
|
|
}
|
|
|
|
|
2021-06-12 03:43:45 +00:00
|
|
|
defer session.resetStatement()
|
|
|
|
|
2020-02-28 12:29:08 +00:00
|
|
|
if session.statement.LastError != nil {
|
|
|
|
return 0, session.statement.LastError
|
2019-01-20 03:45:42 +00:00
|
|
|
}
|
|
|
|
|
2020-02-28 12:29:08 +00:00
|
|
|
v := utils.ReflectValue(bean)
|
2017-01-03 05:31:47 +00:00
|
|
|
t := v.Type()
|
|
|
|
|
|
|
|
// handle before update processors
|
|
|
|
for _, closure := range session.beforeClosures {
|
|
|
|
closure(bean)
|
|
|
|
}
|
|
|
|
cleanupProcessorsClosures(&session.beforeClosures) // cleanup after used
|
|
|
|
if processor, ok := interface{}(bean).(BeforeUpdateProcessor); ok {
|
|
|
|
processor.BeforeUpdate()
|
|
|
|
}
|
|
|
|
// --
|
|
|
|
|
2023-07-25 10:49:55 +00:00
|
|
|
var colNames []string
|
|
|
|
var args []interface{}
|
2017-01-03 05:31:47 +00:00
|
|
|
var err error
|
2022-05-31 03:00:28 +00:00
|
|
|
isMap := t.Kind() == reflect.Map
|
|
|
|
isStruct := t.Kind() == reflect.Struct
|
2017-01-03 05:31:47 +00:00
|
|
|
if isStruct {
|
2020-02-28 12:29:08 +00:00
|
|
|
if err := session.statement.SetRefBean(bean); err != nil {
|
2017-04-10 11:45:00 +00:00
|
|
|
return 0, err
|
|
|
|
}
|
2017-01-03 05:31:47 +00:00
|
|
|
|
2021-12-14 01:00:35 +00:00
|
|
|
if len(session.statement.TableName()) == 0 {
|
2017-01-03 05:31:47 +00:00
|
|
|
return 0, ErrTableNotFound
|
|
|
|
}
|
|
|
|
|
2020-02-28 12:29:08 +00:00
|
|
|
if session.statement.ColumnStr() == "" {
|
2020-03-13 08:57:34 +00:00
|
|
|
colNames, args, err = session.statement.BuildUpdates(v, false, false,
|
2018-04-11 04:57:13 +00:00
|
|
|
false, false, true)
|
2017-01-03 05:31:47 +00:00
|
|
|
} else {
|
2018-04-11 03:52:16 +00:00
|
|
|
colNames, args, err = session.genUpdateColumns(bean)
|
2020-02-28 12:29:08 +00:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
} else if isMap {
|
|
|
|
colNames = make([]string, 0)
|
|
|
|
args = make([]interface{}, 0)
|
|
|
|
bValue := reflect.Indirect(reflect.ValueOf(bean))
|
|
|
|
|
|
|
|
for _, v := range bValue.MapKeys() {
|
2017-07-27 05:32:35 +00:00
|
|
|
colNames = append(colNames, session.engine.Quote(v.String())+" = ?")
|
2017-01-03 05:31:47 +00:00
|
|
|
args = append(args, bValue.MapIndex(v).Interface())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return 0, ErrParamsType
|
|
|
|
}
|
|
|
|
|
2017-07-27 05:32:35 +00:00
|
|
|
table := session.statement.RefTable
|
2017-01-03 05:31:47 +00:00
|
|
|
|
2017-07-27 05:32:35 +00:00
|
|
|
if session.statement.UseAutoTime && table != nil && table.Updated != "" {
|
2020-02-28 12:29:08 +00:00
|
|
|
if !session.statement.ColumnMap.Contain(table.Updated) &&
|
|
|
|
!session.statement.OmitColumnMap.Contain(table.Updated) {
|
2017-09-15 01:51:15 +00:00
|
|
|
colNames = append(colNames, session.engine.Quote(table.Updated)+" = ?")
|
|
|
|
col := table.UpdatedColumn()
|
2021-08-04 08:12:10 +00:00
|
|
|
val, t, err := session.engine.nowTime(col)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2020-07-03 08:10:09 +00:00
|
|
|
if session.engine.dialect.URI().DBType == schemas.ORACLE {
|
|
|
|
args = append(args, t)
|
|
|
|
} else {
|
|
|
|
args = append(args, val)
|
|
|
|
}
|
2017-09-15 01:51:15 +00:00
|
|
|
|
2022-05-31 03:00:28 +00:00
|
|
|
colName := col.Name
|
2017-09-15 01:51:15 +00:00
|
|
|
if isStruct {
|
|
|
|
session.afterClosures = append(session.afterClosures, func(bean interface{}) {
|
|
|
|
col := table.GetColumn(colName)
|
|
|
|
setColumnTime(bean, col, t)
|
|
|
|
})
|
|
|
|
}
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-28 12:29:08 +00:00
|
|
|
if err = session.statement.ProcessIDParam(); err != nil {
|
2017-06-08 11:38:52 +00:00
|
|
|
return 0, err
|
|
|
|
}
|
2017-01-03 05:31:47 +00:00
|
|
|
|
|
|
|
var autoCond builder.Cond
|
2023-07-20 14:45:31 +00:00
|
|
|
if len(condiBean) > 0 {
|
|
|
|
autoCond, err = session.genAutoCond(condiBean[0])
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
2019-03-13 11:02:41 +00:00
|
|
|
}
|
2023-07-20 14:45:31 +00:00
|
|
|
} else if table != nil {
|
|
|
|
if col := table.DeletedColumn(); col != nil && !session.statement.GetUnscoped() { // tag "deleted" is enabled
|
|
|
|
autoCond1 := session.statement.CondDeleted(col)
|
2019-03-13 11:02:41 +00:00
|
|
|
|
2023-07-20 14:45:31 +00:00
|
|
|
if autoCond == nil {
|
|
|
|
autoCond = autoCond1
|
|
|
|
} else {
|
|
|
|
autoCond = autoCond.And(autoCond1)
|
2017-10-30 03:07:56 +00:00
|
|
|
}
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-20 05:24:12 +00:00
|
|
|
var (
|
2020-02-28 12:29:08 +00:00
|
|
|
cond = session.statement.Conds().And(autoCond)
|
|
|
|
doIncVer = isStruct && (table != nil && table.Version != "" && session.statement.CheckVersion)
|
2020-01-20 05:24:12 +00:00
|
|
|
verValue *reflect.Value
|
|
|
|
)
|
2017-03-27 10:29:58 +00:00
|
|
|
if doIncVer {
|
2023-07-25 10:49:55 +00:00
|
|
|
verValue, err = table.VersionColumn().ValueOfV(&v)
|
2017-01-03 05:31:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
2020-01-20 05:24:12 +00:00
|
|
|
if verValue != nil {
|
|
|
|
cond = cond.And(builder.Eq{session.engine.Quote(table.Version): verValue.Interface()})
|
|
|
|
}
|
2017-03-27 10:14:40 +00:00
|
|
|
}
|
2017-01-03 05:31:47 +00:00
|
|
|
|
2022-05-31 03:00:28 +00:00
|
|
|
updateWriter := builder.NewWriter()
|
2023-07-25 10:49:55 +00:00
|
|
|
if err := session.statement.WriteUpdate(updateWriter, cond, v, colNames, args); err != nil {
|
2022-05-31 03:00:28 +00:00
|
|
|
return 0, err
|
|
|
|
}
|
2017-03-27 10:14:40 +00:00
|
|
|
|
2023-07-20 14:45:31 +00:00
|
|
|
tableName := session.statement.TableName() // table name must been get before exec because statement will be reset
|
|
|
|
|
2023-07-24 07:57:05 +00:00
|
|
|
res, err := session.exec(updateWriter.String(), updateWriter.Args()...)
|
2017-01-03 05:31:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
} else if doIncVer {
|
|
|
|
if verValue != nil && verValue.IsValid() && verValue.CanSet() {
|
2018-10-27 13:20:00 +00:00
|
|
|
session.incrVersionFieldValue(verValue)
|
2017-01-03 05:31:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// handle after update processors
|
2017-07-27 05:32:35 +00:00
|
|
|
if session.isAutoCommit {
|
2017-01-03 05:31:47 +00:00
|
|
|
for _, closure := range session.afterClosures {
|
|
|
|
closure(bean)
|
|
|
|
}
|
|
|
|
if processor, ok := interface{}(bean).(AfterUpdateProcessor); ok {
|
2020-02-29 08:59:59 +00:00
|
|
|
session.engine.logger.Debugf("[event] %v has after update processor", tableName)
|
2017-01-03 05:31:47 +00:00
|
|
|
processor.AfterUpdate()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lenAfterClosures := len(session.afterClosures)
|
|
|
|
if lenAfterClosures > 0 {
|
|
|
|
if value, has := session.afterUpdateBeans[bean]; has && value != nil {
|
|
|
|
*value = append(*value, session.afterClosures...)
|
|
|
|
} else {
|
|
|
|
afterClosures := make([]func(interface{}), lenAfterClosures)
|
|
|
|
copy(afterClosures, session.afterClosures)
|
|
|
|
// FIXME: if bean is a map type, it will panic because map cannot be as map key
|
|
|
|
session.afterUpdateBeans[bean] = &afterClosures
|
|
|
|
}
|
|
|
|
} else {
|
2017-01-25 11:54:21 +00:00
|
|
|
if _, ok := interface{}(bean).(AfterUpdateProcessor); ok {
|
2017-01-03 05:31:47 +00:00
|
|
|
session.afterUpdateBeans[bean] = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cleanupProcessorsClosures(&session.afterClosures) // cleanup after used
|
|
|
|
// --
|
|
|
|
|
|
|
|
return res.RowsAffected()
|
|
|
|
}
|
2018-04-11 03:52:16 +00:00
|
|
|
|
|
|
|
func (session *Session) genUpdateColumns(bean interface{}) ([]string, []interface{}, error) {
|
|
|
|
table := session.statement.RefTable
|
|
|
|
colNames := make([]string, 0, len(table.ColumnsSeq()))
|
|
|
|
args := make([]interface{}, 0, len(table.ColumnsSeq()))
|
|
|
|
|
|
|
|
for _, col := range table.Columns() {
|
|
|
|
if !col.IsVersion && !col.IsCreated && !col.IsUpdated {
|
2020-02-28 12:29:08 +00:00
|
|
|
if session.statement.OmitColumnMap.Contain(col.Name) {
|
2018-04-11 03:52:16 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2020-02-24 08:53:18 +00:00
|
|
|
if col.MapType == schemas.ONLYFROMDB {
|
2018-04-11 03:52:16 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
fieldValuePtr, err := col.ValueOf(bean)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
fieldValue := *fieldValuePtr
|
|
|
|
|
2020-03-09 08:03:59 +00:00
|
|
|
if col.IsAutoIncrement && utils.IsValueZero(fieldValue) {
|
|
|
|
continue
|
2018-04-11 03:52:16 +00:00
|
|
|
}
|
|
|
|
|
2020-02-28 12:29:08 +00:00
|
|
|
if (col.IsDeleted && !session.statement.GetUnscoped()) || col.IsCreated {
|
2018-04-11 03:52:16 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-09-23 15:34:26 +00:00
|
|
|
// if only update specify columns
|
2020-02-28 12:29:08 +00:00
|
|
|
if len(session.statement.ColumnMap) > 0 && !session.statement.ColumnMap.Contain(col.Name) {
|
2019-09-23 15:34:26 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-02-28 12:29:08 +00:00
|
|
|
if session.statement.IncrColumns.IsColExist(col.Name) {
|
2019-09-23 15:34:26 +00:00
|
|
|
continue
|
2020-02-28 12:29:08 +00:00
|
|
|
} else if session.statement.DecrColumns.IsColExist(col.Name) {
|
2019-09-23 15:34:26 +00:00
|
|
|
continue
|
2020-02-28 12:29:08 +00:00
|
|
|
} else if session.statement.ExprColumns.IsColExist(col.Name) {
|
2019-09-23 15:34:26 +00:00
|
|
|
continue
|
2018-04-11 03:52:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// !evalphobia! set fieldValue as nil when column is nullable and zero-value
|
2020-02-28 12:29:08 +00:00
|
|
|
if _, ok := getFlagForColumn(session.statement.NullableMap, col); ok {
|
2020-02-26 12:45:10 +00:00
|
|
|
if col.Nullable && utils.IsValueZero(fieldValue) {
|
2018-04-11 03:52:16 +00:00
|
|
|
var nilValue *int
|
|
|
|
fieldValue = reflect.ValueOf(nilValue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if col.IsUpdated && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ {
|
|
|
|
// if time is non-empty, then set to auto time
|
2021-08-04 08:12:10 +00:00
|
|
|
val, t, err := session.engine.nowTime(col)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2018-04-11 03:52:16 +00:00
|
|
|
args = append(args, val)
|
|
|
|
|
2022-05-31 03:00:28 +00:00
|
|
|
colName := col.Name
|
2018-04-11 03:52:16 +00:00
|
|
|
session.afterClosures = append(session.afterClosures, func(bean interface{}) {
|
|
|
|
col := table.GetColumn(colName)
|
|
|
|
setColumnTime(bean, col, t)
|
|
|
|
})
|
2020-02-28 12:29:08 +00:00
|
|
|
} else if col.IsVersion && session.statement.CheckVersion {
|
2018-04-11 03:52:16 +00:00
|
|
|
args = append(args, 1)
|
|
|
|
} else {
|
2020-03-09 08:03:59 +00:00
|
|
|
arg, err := session.statement.Value2Interface(col, fieldValue)
|
2018-04-11 03:52:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return colNames, args, err
|
|
|
|
}
|
|
|
|
args = append(args, arg)
|
|
|
|
}
|
|
|
|
|
|
|
|
colNames = append(colNames, session.engine.Quote(col.Name)+" = ?")
|
|
|
|
}
|
|
|
|
return colNames, args, nil
|
|
|
|
}
|