2020-01-29 14:22:44 +03:00
|
|
|
package gorm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
2020-05-28 08:12:56 +03:00
|
|
|
"errors"
|
2020-06-19 13:30:04 +03:00
|
|
|
"fmt"
|
2020-03-08 08:24:08 +03:00
|
|
|
"reflect"
|
2020-02-22 15:57:29 +03:00
|
|
|
"strings"
|
2020-02-04 03:56:15 +03:00
|
|
|
|
2020-06-02 04:16:07 +03:00
|
|
|
"gorm.io/gorm/clause"
|
2020-09-27 07:25:38 +03:00
|
|
|
"gorm.io/gorm/logger"
|
2020-08-13 13:38:39 +03:00
|
|
|
"gorm.io/gorm/schema"
|
2020-06-30 18:06:48 +03:00
|
|
|
"gorm.io/gorm/utils"
|
2020-01-29 14:22:44 +03:00
|
|
|
)
|
|
|
|
|
2020-02-03 05:40:03 +03:00
|
|
|
// Create insert the value into database
|
2020-03-09 15:37:01 +03:00
|
|
|
func (db *DB) Create(value interface{}) (tx *DB) {
|
2020-02-03 05:40:03 +03:00
|
|
|
tx = db.getInstance()
|
|
|
|
tx.Statement.Dest = value
|
2020-03-09 15:37:01 +03:00
|
|
|
tx.callbacks.Create().Execute(tx)
|
2020-02-03 05:40:03 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save update value in database, if the value doesn't have primary key, will insert it
|
2020-03-09 15:37:01 +03:00
|
|
|
func (db *DB) Save(value interface{}) (tx *DB) {
|
2020-01-29 14:22:44 +03:00
|
|
|
tx = db.getInstance()
|
2020-03-08 08:24:08 +03:00
|
|
|
tx.Statement.Dest = value
|
|
|
|
|
2020-06-09 19:02:14 +03:00
|
|
|
reflectValue := reflect.Indirect(reflect.ValueOf(value))
|
|
|
|
switch reflectValue.Kind() {
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
tx.Statement.UpdatingColumn = true
|
|
|
|
tx.callbacks.Create().Execute(tx)
|
|
|
|
case reflect.Struct:
|
|
|
|
if err := tx.Statement.Parse(value); err == nil && tx.Statement.Schema != nil {
|
2020-08-29 18:02:19 +03:00
|
|
|
for _, pf := range tx.Statement.Schema.PrimaryFields {
|
|
|
|
if _, isZero := pf.ValueOf(reflectValue); isZero {
|
2020-05-24 15:44:37 +03:00
|
|
|
tx.callbacks.Create().Execute(tx)
|
|
|
|
return
|
|
|
|
}
|
2020-03-08 08:24:08 +03:00
|
|
|
}
|
2020-06-06 17:52:08 +03:00
|
|
|
}
|
2020-03-08 08:24:08 +03:00
|
|
|
|
2020-06-09 19:02:14 +03:00
|
|
|
fallthrough
|
|
|
|
default:
|
2020-08-30 05:12:49 +03:00
|
|
|
selectedUpdate := len(tx.Statement.Selects) != 0
|
|
|
|
// when updating, use all fields including those zero-value fields
|
|
|
|
if !selectedUpdate {
|
2020-06-09 19:02:14 +03:00
|
|
|
tx.Statement.Selects = append(tx.Statement.Selects, "*")
|
|
|
|
}
|
|
|
|
|
|
|
|
tx.callbacks.Update().Execute(tx)
|
2020-08-30 05:12:49 +03:00
|
|
|
|
|
|
|
if tx.Error == nil && tx.RowsAffected == 0 && !tx.DryRun && !selectedUpdate {
|
2020-09-06 07:22:05 +03:00
|
|
|
result := reflect.New(tx.Statement.Schema.ModelType).Interface()
|
|
|
|
if err := tx.Session(&Session{WithConditions: true}).First(result).Error; errors.Is(err, ErrRecordNotFound) {
|
2020-08-30 05:12:49 +03:00
|
|
|
return tx.Create(value)
|
|
|
|
}
|
|
|
|
}
|
2020-05-23 18:50:48 +03:00
|
|
|
}
|
2020-06-09 19:02:14 +03:00
|
|
|
|
2020-01-29 14:22:44 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// First find first record that match given conditions, order by primary key
|
2020-05-26 16:30:17 +03:00
|
|
|
func (db *DB) First(dest interface{}, conds ...interface{}) (tx *DB) {
|
2020-06-08 17:32:35 +03:00
|
|
|
tx = db.Limit(1).Order(clause.OrderByColumn{
|
2020-02-04 03:56:15 +03:00
|
|
|
Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},
|
|
|
|
})
|
2020-03-07 08:43:20 +03:00
|
|
|
if len(conds) > 0 {
|
2020-06-08 06:38:51 +03:00
|
|
|
tx.Statement.AddClause(clause.Where{Exprs: tx.Statement.BuildCondition(conds[0], conds[1:]...)})
|
2020-03-07 08:43:20 +03:00
|
|
|
}
|
2020-03-03 09:18:12 +03:00
|
|
|
tx.Statement.RaiseErrorOnNotFound = true
|
2020-05-26 16:30:17 +03:00
|
|
|
tx.Statement.Dest = dest
|
2020-03-09 15:37:01 +03:00
|
|
|
tx.callbacks.Query().Execute(tx)
|
2020-01-29 14:22:44 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Take return a record that match given conditions, the order will depend on the database implementation
|
2020-05-26 16:30:17 +03:00
|
|
|
func (db *DB) Take(dest interface{}, conds ...interface{}) (tx *DB) {
|
2020-06-08 17:32:35 +03:00
|
|
|
tx = db.Limit(1)
|
2020-03-07 08:43:20 +03:00
|
|
|
if len(conds) > 0 {
|
2020-06-08 06:38:51 +03:00
|
|
|
tx.Statement.AddClause(clause.Where{Exprs: tx.Statement.BuildCondition(conds[0], conds[1:]...)})
|
2020-03-07 08:43:20 +03:00
|
|
|
}
|
2020-03-03 09:18:12 +03:00
|
|
|
tx.Statement.RaiseErrorOnNotFound = true
|
2020-05-26 16:30:17 +03:00
|
|
|
tx.Statement.Dest = dest
|
2020-03-09 15:37:01 +03:00
|
|
|
tx.callbacks.Query().Execute(tx)
|
2020-01-29 14:22:44 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Last find last record that match given conditions, order by primary key
|
2020-05-26 16:30:17 +03:00
|
|
|
func (db *DB) Last(dest interface{}, conds ...interface{}) (tx *DB) {
|
2020-06-08 17:32:35 +03:00
|
|
|
tx = db.Limit(1).Order(clause.OrderByColumn{
|
2020-02-23 18:28:35 +03:00
|
|
|
Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},
|
2020-03-04 06:32:36 +03:00
|
|
|
Desc: true,
|
2020-02-23 18:28:35 +03:00
|
|
|
})
|
2020-03-07 08:43:20 +03:00
|
|
|
if len(conds) > 0 {
|
2020-06-08 06:38:51 +03:00
|
|
|
tx.Statement.AddClause(clause.Where{Exprs: tx.Statement.BuildCondition(conds[0], conds[1:]...)})
|
2020-03-07 08:43:20 +03:00
|
|
|
}
|
2020-03-03 09:18:12 +03:00
|
|
|
tx.Statement.RaiseErrorOnNotFound = true
|
2020-05-26 16:30:17 +03:00
|
|
|
tx.Statement.Dest = dest
|
2020-03-09 15:37:01 +03:00
|
|
|
tx.callbacks.Query().Execute(tx)
|
2020-01-29 14:22:44 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find find records that match given conditions
|
2020-05-26 16:30:17 +03:00
|
|
|
func (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB) {
|
2020-01-29 14:22:44 +03:00
|
|
|
tx = db.getInstance()
|
2020-03-07 08:43:20 +03:00
|
|
|
if len(conds) > 0 {
|
2020-06-08 06:38:51 +03:00
|
|
|
tx.Statement.AddClause(clause.Where{Exprs: tx.Statement.BuildCondition(conds[0], conds[1:]...)})
|
2020-03-07 08:43:20 +03:00
|
|
|
}
|
2020-05-26 16:30:17 +03:00
|
|
|
tx.Statement.Dest = dest
|
2020-03-09 15:37:01 +03:00
|
|
|
tx.callbacks.Query().Execute(tx)
|
2020-01-29 14:22:44 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-10 10:36:29 +03:00
|
|
|
// FindInBatches find records in batches
|
|
|
|
func (db *DB) FindInBatches(dest interface{}, batchSize int, fc func(tx *DB, batch int) error) (tx *DB) {
|
|
|
|
tx = db.Session(&Session{WithConditions: true})
|
|
|
|
rowsAffected := int64(0)
|
|
|
|
batch := 0
|
|
|
|
|
|
|
|
for {
|
|
|
|
result := tx.Limit(batchSize).Offset(batch * batchSize).Find(dest)
|
|
|
|
rowsAffected += result.RowsAffected
|
|
|
|
batch++
|
|
|
|
|
|
|
|
if result.Error == nil && result.RowsAffected != 0 {
|
|
|
|
tx.AddError(fc(result, batch))
|
|
|
|
}
|
|
|
|
|
|
|
|
if tx.Error != nil || int(result.RowsAffected) < batchSize {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tx.RowsAffected = rowsAffected
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-08-13 13:38:39 +03:00
|
|
|
func (tx *DB) assignInterfacesToValue(values ...interface{}) {
|
|
|
|
for _, value := range values {
|
|
|
|
switch v := value.(type) {
|
|
|
|
case []clause.Expression:
|
|
|
|
for _, expr := range v {
|
|
|
|
if eq, ok := expr.(clause.Eq); ok {
|
|
|
|
switch column := eq.Column.(type) {
|
|
|
|
case string:
|
|
|
|
if field := tx.Statement.Schema.LookUpField(column); field != nil {
|
|
|
|
tx.AddError(field.Set(tx.Statement.ReflectValue, eq.Value))
|
|
|
|
}
|
|
|
|
case clause.Column:
|
|
|
|
if field := tx.Statement.Schema.LookUpField(column.Name); field != nil {
|
|
|
|
tx.AddError(field.Set(tx.Statement.ReflectValue, eq.Value))
|
|
|
|
}
|
|
|
|
}
|
2020-10-19 09:49:42 +03:00
|
|
|
} else if andCond, ok := expr.(clause.AndConditions); ok {
|
|
|
|
tx.assignInterfacesToValue(andCond.Exprs)
|
2020-05-28 08:12:56 +03:00
|
|
|
}
|
2020-08-13 13:38:39 +03:00
|
|
|
}
|
|
|
|
case clause.Expression, map[string]string, map[interface{}]interface{}, map[string]interface{}:
|
|
|
|
exprs := tx.Statement.BuildCondition(value)
|
|
|
|
tx.assignInterfacesToValue(exprs)
|
|
|
|
default:
|
|
|
|
if s, err := schema.Parse(value, tx.cacheStore, tx.NamingStrategy); err == nil {
|
|
|
|
reflectValue := reflect.Indirect(reflect.ValueOf(value))
|
|
|
|
switch reflectValue.Kind() {
|
|
|
|
case reflect.Struct:
|
|
|
|
for _, f := range s.Fields {
|
|
|
|
if f.Readable {
|
|
|
|
if v, isZero := f.ValueOf(reflectValue); !isZero {
|
|
|
|
if field := tx.Statement.Schema.LookUpField(f.Name); field != nil {
|
|
|
|
tx.AddError(field.Set(tx.Statement.ReflectValue, v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-28 08:12:56 +03:00
|
|
|
}
|
2020-08-13 13:38:39 +03:00
|
|
|
} else if len(values) > 0 {
|
|
|
|
exprs := tx.Statement.BuildCondition(values[0], values[1:]...)
|
|
|
|
tx.assignInterfacesToValue(exprs)
|
|
|
|
return
|
2020-05-28 08:12:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (db *DB) FirstOrInit(dest interface{}, conds ...interface{}) (tx *DB) {
|
2020-06-08 17:32:35 +03:00
|
|
|
if tx = db.First(dest, conds...); errors.Is(tx.Error, ErrRecordNotFound) {
|
2020-05-28 08:12:56 +03:00
|
|
|
if c, ok := tx.Statement.Clauses["WHERE"]; ok {
|
|
|
|
if where, ok := c.Expression.(clause.Where); ok {
|
2020-08-13 13:38:39 +03:00
|
|
|
tx.assignInterfacesToValue(where.Exprs)
|
2020-05-28 08:12:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize with attrs, conds
|
|
|
|
if len(tx.Statement.attrs) > 0 {
|
2020-08-13 13:38:39 +03:00
|
|
|
tx.assignInterfacesToValue(tx.Statement.attrs...)
|
2020-05-28 08:12:56 +03:00
|
|
|
}
|
|
|
|
tx.Error = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize with attrs, conds
|
|
|
|
if len(tx.Statement.assigns) > 0 {
|
2020-08-13 13:38:39 +03:00
|
|
|
tx.assignInterfacesToValue(tx.Statement.assigns...)
|
2020-05-28 08:12:56 +03:00
|
|
|
}
|
2020-01-29 14:22:44 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-28 11:10:10 +03:00
|
|
|
func (db *DB) FirstOrCreate(dest interface{}, conds ...interface{}) (tx *DB) {
|
2020-06-08 17:32:35 +03:00
|
|
|
if tx = db.First(dest, conds...); errors.Is(tx.Error, ErrRecordNotFound) {
|
2020-05-28 11:10:10 +03:00
|
|
|
tx.Error = nil
|
|
|
|
|
|
|
|
if c, ok := tx.Statement.Clauses["WHERE"]; ok {
|
|
|
|
if where, ok := c.Expression.(clause.Where); ok {
|
2020-08-13 13:38:39 +03:00
|
|
|
tx.assignInterfacesToValue(where.Exprs)
|
2020-05-28 11:10:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize with attrs, conds
|
|
|
|
if len(tx.Statement.attrs) > 0 {
|
2020-08-13 13:38:39 +03:00
|
|
|
tx.assignInterfacesToValue(tx.Statement.attrs...)
|
2020-05-28 11:10:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// initialize with attrs, conds
|
|
|
|
if len(tx.Statement.assigns) > 0 {
|
2020-08-13 13:38:39 +03:00
|
|
|
tx.assignInterfacesToValue(tx.Statement.assigns...)
|
2020-05-28 11:10:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return tx.Create(dest)
|
2020-06-08 17:32:35 +03:00
|
|
|
} else if len(db.Statement.assigns) > 0 {
|
2020-06-08 06:38:51 +03:00
|
|
|
exprs := tx.Statement.BuildCondition(tx.Statement.assigns[0], tx.Statement.assigns[1:]...)
|
2020-05-28 11:10:10 +03:00
|
|
|
assigns := map[string]interface{}{}
|
|
|
|
for _, expr := range exprs {
|
|
|
|
if eq, ok := expr.(clause.Eq); ok {
|
|
|
|
switch column := eq.Column.(type) {
|
|
|
|
case string:
|
|
|
|
assigns[column] = eq.Value
|
|
|
|
case clause.Column:
|
|
|
|
assigns[column.Name] = eq.Value
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return tx.Model(dest).Updates(assigns)
|
|
|
|
}
|
2020-05-28 08:12:56 +03:00
|
|
|
|
2020-06-08 17:32:35 +03:00
|
|
|
return db
|
2020-01-29 14:22:44 +03:00
|
|
|
}
|
|
|
|
|
2020-06-15 07:28:35 +03:00
|
|
|
// Update update attributes with callbacks, refer: https://gorm.io/docs/update.html#Update-Changed-Fields
|
2020-03-09 15:37:01 +03:00
|
|
|
func (db *DB) Update(column string, value interface{}) (tx *DB) {
|
2020-01-29 14:22:44 +03:00
|
|
|
tx = db.getInstance()
|
2020-03-07 08:43:20 +03:00
|
|
|
tx.Statement.Dest = map[string]interface{}{column: value}
|
2020-03-09 15:37:01 +03:00
|
|
|
tx.callbacks.Update().Execute(tx)
|
2020-01-29 14:22:44 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-15 07:28:35 +03:00
|
|
|
// Updates update attributes with callbacks, refer: https://gorm.io/docs/update.html#Update-Changed-Fields
|
2020-03-09 15:37:01 +03:00
|
|
|
func (db *DB) Updates(values interface{}) (tx *DB) {
|
2020-01-29 14:22:44 +03:00
|
|
|
tx = db.getInstance()
|
2020-03-07 08:43:20 +03:00
|
|
|
tx.Statement.Dest = values
|
2020-03-09 15:37:01 +03:00
|
|
|
tx.callbacks.Update().Execute(tx)
|
2020-01-29 14:22:44 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-03-09 15:37:01 +03:00
|
|
|
func (db *DB) UpdateColumn(column string, value interface{}) (tx *DB) {
|
2020-01-29 14:22:44 +03:00
|
|
|
tx = db.getInstance()
|
2020-03-07 08:43:20 +03:00
|
|
|
tx.Statement.Dest = map[string]interface{}{column: value}
|
2020-06-05 15:24:15 +03:00
|
|
|
tx.Statement.UpdatingColumn = true
|
2020-03-09 15:37:01 +03:00
|
|
|
tx.callbacks.Update().Execute(tx)
|
2020-01-29 14:22:44 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-03-09 15:37:01 +03:00
|
|
|
func (db *DB) UpdateColumns(values interface{}) (tx *DB) {
|
2020-01-29 14:22:44 +03:00
|
|
|
tx = db.getInstance()
|
2020-03-07 08:43:20 +03:00
|
|
|
tx.Statement.Dest = values
|
2020-06-05 15:24:15 +03:00
|
|
|
tx.Statement.UpdatingColumn = true
|
2020-03-09 15:37:01 +03:00
|
|
|
tx.callbacks.Update().Execute(tx)
|
2020-01-29 14:22:44 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-03 05:40:03 +03:00
|
|
|
// Delete delete value match given conditions, if the value has primary key, then will including the primary key as condition
|
2020-03-09 15:37:01 +03:00
|
|
|
func (db *DB) Delete(value interface{}, conds ...interface{}) (tx *DB) {
|
2020-01-29 14:22:44 +03:00
|
|
|
tx = db.getInstance()
|
2020-03-08 09:51:52 +03:00
|
|
|
if len(conds) > 0 {
|
2020-06-08 06:38:51 +03:00
|
|
|
tx.Statement.AddClause(clause.Where{Exprs: tx.Statement.BuildCondition(conds[0], conds[1:]...)})
|
2020-03-08 09:51:52 +03:00
|
|
|
}
|
|
|
|
tx.Statement.Dest = value
|
2020-03-09 15:37:01 +03:00
|
|
|
tx.callbacks.Delete().Execute(tx)
|
2020-01-29 14:22:44 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-24 06:32:59 +03:00
|
|
|
func (db *DB) Count(count *int64) (tx *DB) {
|
2020-01-29 14:22:44 +03:00
|
|
|
tx = db.getInstance()
|
2020-05-24 06:32:59 +03:00
|
|
|
if tx.Statement.Model == nil {
|
|
|
|
tx.Statement.Model = tx.Statement.Dest
|
2020-08-19 15:30:39 +03:00
|
|
|
defer func() {
|
|
|
|
tx.Statement.Model = nil
|
|
|
|
}()
|
2020-05-24 06:32:59 +03:00
|
|
|
}
|
2020-06-05 14:19:08 +03:00
|
|
|
|
|
|
|
if len(tx.Statement.Selects) == 0 {
|
|
|
|
tx.Statement.AddClause(clause.Select{Expression: clause.Expr{SQL: "count(1)"}})
|
2020-08-18 13:58:53 +03:00
|
|
|
defer delete(tx.Statement.Clauses, "SELECT")
|
2020-06-23 03:51:01 +03:00
|
|
|
} else if !strings.Contains(strings.ToLower(tx.Statement.Selects[0]), "count(") {
|
|
|
|
expr := clause.Expr{SQL: "count(1)"}
|
|
|
|
|
|
|
|
if len(tx.Statement.Selects) == 1 {
|
2020-08-03 05:30:25 +03:00
|
|
|
dbName := tx.Statement.Selects[0]
|
2020-06-23 03:51:01 +03:00
|
|
|
if tx.Statement.Parse(tx.Statement.Model) == nil {
|
2020-08-03 05:30:25 +03:00
|
|
|
if f := tx.Statement.Schema.LookUpField(dbName); f != nil {
|
|
|
|
dbName = f.DBName
|
2020-06-23 03:51:01 +03:00
|
|
|
}
|
2020-06-05 14:19:08 +03:00
|
|
|
}
|
2020-08-03 05:30:25 +03:00
|
|
|
|
|
|
|
if tx.Statement.Distinct {
|
|
|
|
expr = clause.Expr{SQL: "COUNT(DISTINCT(?))", Vars: []interface{}{clause.Column{Name: dbName}}}
|
|
|
|
} else {
|
|
|
|
expr = clause.Expr{SQL: "COUNT(?)", Vars: []interface{}{clause.Column{Name: dbName}}}
|
|
|
|
}
|
2020-06-05 14:19:08 +03:00
|
|
|
}
|
2020-06-23 03:51:01 +03:00
|
|
|
|
|
|
|
tx.Statement.AddClause(clause.Select{Expression: expr})
|
2020-07-01 05:19:52 +03:00
|
|
|
defer tx.Statement.AddClause(clause.Select{})
|
2020-06-05 14:19:08 +03:00
|
|
|
}
|
|
|
|
|
2020-10-22 06:28:43 +03:00
|
|
|
if orderByClause, ok := db.Statement.Clauses["ORDER BY"]; ok {
|
|
|
|
if _, ok := db.Statement. |