2020-04-16 05:29:18 +03:00
|
|
|
package callbacks
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
2020-11-17 16:49:40 +03:00
|
|
|
"strings"
|
2020-04-16 05:29:18 +03:00
|
|
|
|
2020-06-02 04:16:07 +03:00
|
|
|
"gorm.io/gorm"
|
|
|
|
"gorm.io/gorm/clause"
|
2020-09-03 05:58:48 +03:00
|
|
|
"gorm.io/gorm/schema"
|
2021-11-29 13:34:50 +03:00
|
|
|
"gorm.io/gorm/utils"
|
2020-04-16 05:29:18 +03:00
|
|
|
)
|
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
func SaveBeforeAssociations(create bool) func(db *gorm.DB) {
|
|
|
|
return func(db *gorm.DB) {
|
|
|
|
if db.Error == nil && db.Statement.Schema != nil {
|
|
|
|
selectColumns, restricted := db.Statement.SelectAndOmitColumns(create, !create)
|
|
|
|
|
|
|
|
// Save Belongs To associations
|
|
|
|
for _, rel := range db.Statement.Schema.Relationships.BelongsTo {
|
|
|
|
if v, ok := selectColumns[rel.Name]; (ok && !v) || (!ok && restricted) {
|
|
|
|
continue
|
|
|
|
}
|
2020-06-01 14:41:33 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
setupReferences := func(obj reflect.Value, elem reflect.Value) {
|
|
|
|
for _, ref := range rel.References {
|
|
|
|
if !ref.OwnPrimaryKey {
|
2022-02-16 10:30:43 +03:00
|
|
|
pv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, elem)
|
|
|
|
db.AddError(ref.ForeignKey.Set(db.Statement.Context, obj, pv))
|
2021-02-07 09:24:11 +03:00
|
|
|
|
|
|
|
if dest, ok := db.Statement.Dest.(map[string]interface{}); ok {
|
|
|
|
dest[ref.ForeignKey.DBName] = pv
|
|
|
|
if _, ok := dest[rel.Name]; ok {
|
|
|
|
dest[rel.Name] = elem.Interface()
|
|
|
|
}
|
2020-06-01 14:41:33 +03:00
|
|
|
}
|
|
|
|
}
|
2020-04-20 06:47:29 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
switch db.Statement.ReflectValue.Kind() {
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
var (
|
2021-11-29 04:33:20 +03:00
|
|
|
rValLen = db.Statement.ReflectValue.Len()
|
|
|
|
objs = make([]reflect.Value, 0, rValLen)
|
2021-02-07 09:24:11 +03:00
|
|
|
fieldType = rel.Field.FieldType
|
|
|
|
isPtr = fieldType.Kind() == reflect.Ptr
|
|
|
|
)
|
2020-04-16 05:29:18 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
if !isPtr {
|
|
|
|
fieldType = reflect.PtrTo(fieldType)
|
|
|
|
}
|
2020-08-17 12:41:36 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
|
2023-04-11 06:06:13 +03:00
|
|
|
distinctElems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
|
|
|
|
identityMap := map[string]bool{}
|
2021-11-29 04:33:20 +03:00
|
|
|
for i := 0; i < rValLen; i++ {
|
2021-02-07 09:24:11 +03:00
|
|
|
obj := db.Statement.ReflectValue.Index(i)
|
2021-11-29 04:33:20 +03:00
|
|
|
if reflect.Indirect(obj).Kind() != reflect.Struct {
|
|
|
|
break
|
|
|
|
}
|
2022-02-16 10:30:43 +03:00
|
|
|
if _, zero := rel.Field.ValueOf(db.Statement.Context, obj); !zero { // check belongs to relation value
|
|
|
|
rv := rel.Field.ReflectValueOf(db.Statement.Context, obj) // relation reflect value
|
2023-04-11 06:06:13 +03:00
|
|
|
if !isPtr {
|
|
|
|
rv = rv.Addr()
|
|
|
|
}
|
2021-11-29 04:33:20 +03:00
|
|
|
objs = append(objs, obj)
|
2023-04-11 06:06:13 +03:00
|
|
|
elems = reflect.Append(elems, rv)
|
|
|
|
|
|
|
|
relPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields))
|
|
|
|
for _, pf := range rel.FieldSchema.PrimaryFields {
|
|
|
|
if pfv, ok := pf.ValueOf(db.Statement.Context, rv); !ok {
|
|
|
|
relPrimaryValues = append(relPrimaryValues, pfv)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cacheKey := utils.ToStringKey(relPrimaryValues...)
|
|
|
|
if len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {
|
|
|
|
if cacheKey != "" { // has primary fields
|
|
|
|
identityMap[cacheKey] = true
|
|
|
|
}
|
|
|
|
|
|
|
|
distinctElems = reflect.Append(distinctElems, rv)
|
2020-08-17 12:41:36 +03:00
|
|
|
}
|
2020-04-16 05:29:18 +03:00
|
|
|
}
|
2020-04-19 18:11:56 +03:00
|
|
|
}
|
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
if elems.Len() > 0 {
|
2023-04-11 06:06:13 +03:00
|
|
|
if saveAssociations(db, rel, distinctElems, selectColumns, restricted, nil) == nil {
|
2021-02-07 09:24:11 +03:00
|
|
|
for i := 0; i < elems.Len(); i++ {
|
|
|
|
setupReferences(objs[i], elems.Index(i))
|
|
|
|
}
|
2020-04-16 05:29:18 +03:00
|
|
|
}
|
|
|
|
}
|
2021-02-07 09:24:11 +03:00
|
|
|
case reflect.Struct:
|
2022-02-16 10:30:43 +03:00
|
|
|
if _, zero := rel.Field.ValueOf(db.Statement.Context, db.Statement.ReflectValue); !zero {
|
|
|
|
rv := rel.Field.ReflectValueOf(db.Statement.Context, db.Statement.ReflectValue) // relation reflect value
|
2021-02-07 09:24:11 +03:00
|
|
|
if rv.Kind() != reflect.Ptr {
|
|
|
|
rv = rv.Addr()
|
|
|
|
}
|
2020-04-16 05:29:18 +03:00
|
|
|
|
2022-03-17 18:53:31 +03:00
|
|
|
if saveAssociations(db, rel, rv, selectColumns, restricted, nil) == nil {
|
2021-02-07 09:24:11 +03:00
|
|
|
setupReferences(db.Statement.ReflectValue, rv)
|
|
|
|
}
|
2020-04-16 05:29:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
func SaveAfterAssociations(create bool) func(db *gorm.DB) {
|
|
|
|
return func(db *gorm.DB) {
|
|
|
|
if db.Error == nil && db.Statement.Schema != nil {
|
|
|
|
selectColumns, restricted := db.Statement.SelectAndOmitColumns(create, !create)
|
2020-04-19 18:11:56 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
// Save Has One associations
|
|
|
|
for _, rel := range db.Statement.Schema.Relationships.HasOne {
|
|
|
|
if v, ok := selectColumns[rel.Name]; (ok && !v) || (!ok && restricted) {
|
|
|
|
continue
|
|
|
|
}
|
2020-04-19 18:11:56 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
switch db.Statement.ReflectValue.Kind() {
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
var (
|
|
|
|
fieldType = rel.Field.FieldType
|
|
|
|
isPtr = fieldType.Kind() == reflect.Ptr
|
|
|
|
)
|
2020-04-19 18:11:56 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
if !isPtr {
|
|
|
|
fieldType = reflect.PtrTo(fieldType)
|
|
|
|
}
|
2020-04-17 03:23:47 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
|
2020-04-19 18:11:56 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
|
|
|
|
obj := db.Statement.ReflectValue.Index(i)
|
2020-04-20 06:47:29 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
if reflect.Indirect(obj).Kind() == reflect.Struct {
|
2022-02-16 10:30:43 +03:00
|
|
|
if _, zero := rel.Field.ValueOf(db.Statement.Context, obj); !zero {
|
|
|
|
rv := rel.Field.ReflectValueOf(db.Statement.Context, obj)
|
2021-02-07 09:24:11 +03:00
|
|
|
if rv.Kind() != reflect.Ptr {
|
|
|
|
rv = rv.Addr()
|
|
|
|
}
|
2020-04-20 06:47:29 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
for _, ref := range rel.References {
|
|
|
|
if ref.OwnPrimaryKey {
|
2022-02-16 10:30:43 +03:00
|
|
|
fv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, obj)
|
|
|
|
db.AddError(ref.ForeignKey.Set(db.Statement.Context, rv, fv))
|
2021-02-07 09:24:11 +03:00
|
|
|
} else if ref.PrimaryValue != "" {
|
2022-02-16 10:30:43 +03:00
|
|
|
db.AddError(ref.ForeignKey.Set(db.Statement.Context, rv, ref.PrimaryValue))
|
2021-02-07 09:24:11 +03:00
|
|
|
}
|
2020-08-17 12:41:36 +03:00
|
|
|
}
|
2020-04-19 18:11:56 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
elems = reflect.Append(elems, rv)
|
|
|
|
}
|
2020-08-17 12:41:36 +03:00
|
|
|
}
|
2020-04-19 18:11:56 +03:00
|
|
|
}
|
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
if elems.Len() > 0 {
|
2021-03-04 14:44:15 +03:00
|
|
|
assignmentColumns := make([]string, 0, len(rel.References))
|
2021-02-07 09:24:11 +03:00
|
|
|
for _, ref := range rel.References {
|
|
|
|
assignmentColumns = append(assignmentColumns, ref.ForeignKey.DBName)
|
|
|
|
}
|
2020-06-20 05:51:36 +03:00
|
|
|
|
2022-03-17 18:53:31 +03:00
|
|
|
saveAssociations(db, rel, elems, selectColumns, restricted, assignmentColumns)
|
2020-04-20 06:47:29 +03:00
|
|
|
}
|
2021-02-07 09:24:11 +03:00
|
|
|
case reflect.Struct:
|
2022-02-16 10:30:43 +03:00
|
|
|
if _, zero := rel.Field.ValueOf(db.Statement.Context, db.Statement.ReflectValue); !zero {
|
|
|
|
f := rel.Field.ReflectValueOf(db.Statement.Context, db.Statement.ReflectValue)
|
2021-02-07 09:24:11 +03:00
|
|
|
if f.Kind() != reflect.Ptr {
|
|
|
|
f = f.Addr()
|
|
|
|
}
|
2020-04-17 03:23:47 +03:00
|
|
|
|
2021-03-04 14:44:15 +03:00
|
|
|
assignmentColumns := make([]string, 0, len(rel.References))
|
2021-02-07 09:24:11 +03:00
|
|
|
for _, ref := range rel.References {
|
|
|
|
if ref.OwnPrimaryKey {
|
2022-02-16 10:30:43 +03:00
|
|
|
fv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, db.Statement.ReflectValue)
|
2022-03-23 12:24:25 +03:00
|
|
|
db.AddError(ref.ForeignKey.Set(db.Statement.Context, f, fv))
|
2021-02-07 09:24:11 +03:00
|
|
|
} else if ref.PrimaryValue != "" {
|
2022-03-23 12:24:25 +03:00
|
|
|
db.AddError(ref.ForeignKey.Set(db.Statement.Context, f, ref.PrimaryValue))
|
2021-02-07 09:24:11 +03:00
|
|
|
}
|
|
|
|
assignmentColumns = append(assignmentColumns, ref.ForeignKey.DBName)
|
2020-04-17 03:23:47 +03:00
|
|
|
}
|
2020-04-19 18:11:56 +03:00
|
|
|
|
2022-03-17 18:53:31 +03:00
|
|
|
saveAssociations(db, rel, f, selectColumns, restricted, assignmentColumns)
|
2021-02-07 09:24:11 +03:00
|
|
|
}
|
2020-04-17 03:23:47 +03:00
|
|
|
}
|
2020-04-19 18:11:56 +03:00
|
|
|
}
|
2020-04-17 03:23:47 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
// Save Has Many associations
|
|
|
|
for _, rel := range db.Statement.Schema.Relationships.HasMany {
|
|
|
|
if v, ok := selectColumns[rel.Name]; (ok && !v) || (!ok && restricted) {
|
|
|
|
continue
|
|
|
|
}
|
2020-04-17 03:23:47 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
fieldType := rel.Field.IndirectFieldType.Elem()
|
|
|
|
isPtr := fieldType.Kind() == reflect.Ptr
|
|
|
|
if !isPtr {
|
|
|
|
fieldType = reflect.PtrTo(fieldType)
|
|
|
|
}
|
|
|
|
elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
|
2021-11-29 13:34:50 +03:00
|
|
|
identityMap := map[string]bool{}
|
2021-02-07 09:24:11 +03:00
|
|
|
appendToElems := func(v reflect.Value) {
|
2022-02-16 10:30:43 +03:00
|
|
|
if _, zero := rel.Field.ValueOf(db.Statement.Context, v); !zero {
|
|
|
|
f := reflect.Indirect(rel.Field.ReflectValueOf(db.Statement.Context, v))
|
2020-04-19 18:11:56 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
for i := 0; i < f.Len(); i++ {
|
|
|
|
elem := f.Index(i)
|
|
|
|
for _, ref := range rel.References {
|
|
|
|
if ref.OwnPrimaryKey {
|
2022-02-16 10:30:43 +03:00
|
|
|
pv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, v)
|
2022-03-23 12:24:25 +03:00
|
|
|
db.AddError(ref.ForeignKey.Set(db.Statement.Context, elem, pv))
|
2021-02-07 09:24:11 +03:00
|
|
|
} else if ref.PrimaryValue != "" {
|
2022-03-23 12:24:25 +03:00
|
|
|
db.AddError(ref.ForeignKey.Set(db.Statement.Context, elem, ref.PrimaryValue))
|
2021-02-07 09:24:11 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-29 13:34:50 +03:00
|
|
|
relPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields))
|
|
|
|
for _, pf := range rel.FieldSchema.PrimaryFields {
|
2022-02-16 10:30:43 +03:00
|
|
|
if pfv, ok := pf.ValueOf(db.Statement.Context, elem); !ok {
|
2021-11-29 13:34:50 +03:00
|
|
|
relPrimaryValues = append(relPrimaryValues, pfv)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-14 15:05:22 +03:00
|
|
|
cacheKey := utils.ToStringKey(relPrimaryValues...)
|
2021-12-02 05:20:16 +03:00
|
|
|
if len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {
|
2022-10-18 06:58:42 +03:00
|
|
|
if cacheKey != "" { // has primary fields
|
|
|
|
identityMap[cacheKey] = true
|
|
|
|
}
|
|
|
|
|
2021-11-29 13:34:50 +03:00
|
|
|
if isPtr {
|
|
|
|
elems = reflect.Append(elems, elem)
|
|
|
|
} else {
|
|
|
|
elems = reflect.Append(elems, elem.Addr())
|
|
|
|
}
|
2020-04-19 18:11:56 +03:00
|
|
|
}
|
|
|
|
}
|
2021-02-07 09:24:11 +03:00
|
|
|
}
|
|
|
|
}
|
2020-04-19 18:11:56 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
switch db.Statement.ReflectValue.Kind() {
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
|
|
|
|
obj := db.Statement.ReflectValue.Index(i)
|
|
|
|
if reflect.Indirect(obj).Kind() == reflect.Struct {
|
|
|
|
appendToElems(obj)
|
2020-04-19 18:11:56 +03:00
|
|
|
}
|
2020-04-17 03:23:47 +03:00
|
|
|
}
|
2021-02-07 09:24:11 +03:00
|
|
|
case reflect.Struct:
|
|
|
|
appendToElems(db.Statement.ReflectValue)
|
2020-04-17 03:23:47 +03:00
|
|
|
}
|
2020-04-19 09:29:31 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
if elems.Len() > 0 {
|
2021-03-04 14:44:15 +03:00
|
|
|
assignmentColumns := make([]string, 0, len(rel.References))
|
2021-02-07 09:24:11 +03:00
|
|
|
for _, ref := range rel.References {
|
|
|
|
assignmentColumns = append(assignmentColumns, ref.ForeignKey.DBName)
|
2020-08-17 12:41:36 +03:00
|
|
|
}
|
2021-02-07 09:24:11 +03:00
|
|
|
|
2022-03-17 18:53:31 +03:00
|
|
|
saveAssociations(db, rel, elems, selectColumns, restricted, assignmentColumns)
|
2020-04-19 18:11:56 +03:00
|
|
|
}
|
|
|
|
}
|
2020-04-19 09:29:31 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
// Save Many2Many associations
|
|
|
|
for _, rel := range db.Statement.Schema.Relationships.Many2Many {
|
|
|
|
if v, ok := selectColumns[rel.Name]; (ok && !v) || (!ok && restricted) {
|
|
|
|
continue
|
2020-06-20 05:51:36 +03:00
|
|
|
}
|
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
fieldType := rel.Field.IndirectFieldType.Elem()
|
|
|
|
isPtr := fieldType.Kind() == reflect.Ptr
|
|
|
|
if !isPtr {
|
|
|
|
fieldType = reflect.PtrTo(fieldType)
|
|
|
|
}
|
|
|
|
elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
|
2022-07-01 10:12:15 +03:00
|
|
|
distinctElems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
|
2021-02-07 09:24:11 +03:00
|
|
|
joins := reflect.MakeSlice(reflect.SliceOf(reflect.PtrTo(rel.JoinTable.ModelType)), 0, 10)
|
|
|
|
objs := []reflect.Value{}
|
2020-04-19 09:29:31 +03:00
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
appendToJoins := func(obj reflect.Value, elem reflect.Value) {
|
|
|
|
joinValue := reflect.New(rel.JoinTable.ModelType)
|
|
|
|
for _, ref := range rel.References {
|
|
|
|
if ref.OwnPrimaryKey {
|
2022-02-16 10:30:43 +03:00
|
|
|
fv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, obj)
|
2022-03-23 12:24:25 +03:00
|
|
|
db.AddError(ref.ForeignKey.Set(db.Statement.Context, joinValue, fv))
|
2021-02-07 09:24:11 +03:00
|
|
|
} else if ref.PrimaryValue != "" {
|
2022-03-23 12:24:25 +03:00
|
|
|
db.AddError(ref.ForeignKey.Set(db.Statement.Context, joinValue, ref.PrimaryValue))
|
2021-02-07 09:24:11 +03:00
|
|
|
} else {
|
2022-02-16 10:30:43 +03:00
|
|
|
fv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, elem)
|
2022-03-23 12:24:25 +03:00
|
|
|
db.AddError(ref.ForeignKey.Set(db.Statement.Context, joinValue, fv))
|
2021-02-07 09:24:11 +03:00
|
|
|
}
|
2020-04-20 06:47:29 +03:00
|
|
|
}
|
2021-02-07 09:24:11 +03:00
|
|
|
joins = reflect.Append(joins, joinValue)
|
2020-04-19 18:11:56 +03:00
|
|
|
}
|
2020-04-20 06:47:29 +03:00
|
|
|
|
2022-07-01 10:12:15 +03:00
|
|
|
identityMap := map[string]bool{}
|
2021-02-07 09:24:11 +03:00
|
|
|
appendToElems := func(v reflect.Value) {
|
2022-02-16 10:30:43 +03:00
|
|
|
if _, zero := rel.Field.ValueOf(db.Statement.Context, v); !zero {
|
|
|
|
f := reflect.Indirect(rel.Field.ReflectValueOf(db.Statement.Context, v))
|
2021-02-07 09:24:11 +03:00
|
|
|
for i := 0; i < f.Len(); i++ {
|
|
|
|
elem := f.Index(i)
|
2022-07-01 10:12:15 +03:00
|
|
|
if !isPtr {
|
|
|
|
elem = elem.Addr()
|
|
|
|
}
|
2021-02-07 09:24:11 +03:00
|
|
|
objs = append(objs, v)
|
2022-07-01 10:12:15 +03:00
|
|
|
elems = reflect.Append(elems, elem)
|
|
|
|
|
|
|
|
relPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields))
|
|
|
|
for _, pf := range rel.FieldSchema.PrimaryFields {
|
|
|
|
if pfv, ok := pf.ValueOf(db.Statement.Context, elem); !ok {
|
|
|
|
relPrimaryValues = append(relPrimaryValues, pfv)
|
|
|
|
}
|
2021-02-07 09:24:11 +03:00
|
|
|
}
|
2022-07-01 10:12:15 +03:00
|
|
|
|
2022-07-14 15:05:22 +03:00
|
|
|
cacheKey := utils.ToStringKey(relPrimaryValues...)
|
2022-07-01 10:12:15 +03:00
|
|
|
if len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {
|
2022-10-18 06:58:42 +03:00
|
|
|
if cacheKey != "" { // has primary fields
|
|
|
|
identityMap[cacheKey] = true
|
|
|
|
}
|
|
|
|
|
2022-07-01 10:12:15 +03:00
|
|
|
distinctElems = reflect.Append(distinctElems, elem)
|
|
|
|
}
|
|
|
|
|
2020-04-19 09:29:31 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
switch db.Statement.ReflectValue.Kind() {
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
|
|
|
|
obj := db.Statement.ReflectValue.Index(i)
|
|
|
|
if reflect.Indirect(obj).Kind() == reflect.Struct {
|
|
|
|
appendToElems(obj)
|
|
|
|
}
|
2020-08-17 12:41:36 +03:00
|
|
|
}
|
2021-02-07 09:24:11 +03:00
|
|
|
case reflect.Struct:
|
|
|
|
appendToElems(db.Statement.ReflectValue)
|
2020-04-20 06:47:29 +03:00
|
|
|
}
|
|
|
|
|
2021-04-14 08:00:54 +03:00
|
|
|
// optimize elems of reflect value length
|
|
|
|
if elemLen := elems.Len(); elemLen > 0 {
|
2021-02-07 09:24:11 +03:00
|
|
|
if v, ok := selectColumns[rel.Name+".*"]; !ok || v {
|
2022-07-01 10:12:15 +03:00
|
|
|
saveAssociations(db, rel, distinctElems, selectColumns, restricted, nil)
|
2021-02-07 09:24:11 +03:00
|
|
|
}
|
2020-04-20 06:47:29 +03:00
|
|
|
|
2021-04-14 08:00:54 +03:00
|
|
|
for i := 0; i < elemLen; i++ {
|
2021-02-07 09:24:11 +03:00
|
|
|
appendToJoins(objs[i], elems.Index(i))
|
|
|
|
}
|
2020-04-20 06:47:29 +03:00
|
|
|
}
|
|
|
|
|
2021-02-07 09:24:11 +03:00
|
|
|
if joins.Len() > 0 {
|
|
|
|
db.AddError(db.Session(&gorm.Session{NewDB: true}).Clauses(clause.OnConflict{DoNothing: true}).Session(&gorm.Session{
|
|
|
|
SkipHooks: db.Statement.SkipHooks,
|
|
|
|
DisableNestedTransaction: true,
|
|
|
|
}).Create(joins.Interface()).Error)
|
|
|
|
}
|
2020-04-19 18:11:56 +03:00
|
|
|
}
|
2020-04-19 09:29:31 +03:00
|
|
|
}
|
|
|
|
}
|
2020-04-17 03:23:47 +03:00
|
|
|
}
|
2020-09-03 05:58:48 +03:00
|
|
|
|
2022-03-20 04:02:45 +03:00
|
|
|
func onConflictOption(stmt *gorm.Statement, s *schema.Schema, defaultUpdatingColumns []string) (onConflict clause.OnConflict) {
|
2021-08-26 08:37:26 +03:00
|
|
|
if len(defaultUpdatingColumns) > 0 || stmt.DB.FullSaveAssociations {
|
|
|
|
onConflict.Columns = make([]clause.Column, 0, len(s.PrimaryFieldDBNames))
|
2020-12-03 10:00:26 +03:00
|
|
|
for _, dbName := range s.PrimaryFieldDBNames {
|
2021-08-26 08:37:26 +03:00
|
|
|
onConflict.Columns = append(onConflict.Columns, clause.Column{Name: dbName})
|
2020-09-24 14:28:52 +03:00
|
|
|
}
|
|
|
|
|
2021-08-26 08:37:26 +03:00
|
|
|
onConflict.UpdateAll = stmt.DB.FullSaveAssociations
|
|
|
|
if !onConflict.UpdateAll {
|
|
|
|
onConflict.DoUpdates = clause.AssignmentColumns(defaultUpdatingColumns)
|
2020-09-24 14:28:52 +03:00
|
|
|
}
|
2021-08-26 08:37:26 +03:00
|
|
|
} else {
|
|
|
|
onConflict.DoNothing = true
|
2020-09-03 05:58:48 +03:00
|
|
|
}
|
2020-09-24 14:28:52 +03:00
|
|
|
|
2021-08-26 08:37:26 +03:00
|
|
|
return
|
2020-09-03 05:58:48 +03:00
|
|
|
}
|
2020-11-17 16:49:40 +03:00
|
|
|
|
2022-03-17 18:53:31 +03:00
|
|
|
func saveAssociations(db *gorm.DB, rel *schema.Relationship, rValues reflect.Value, selectColumns map[string]bool, restricted bool, defaultUpdatingColumns []string) error {
|
|
|
|
// stop save association loop
|
|
|
|
if checkAssociationsSaved(db, rValues) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-18 14:06:49 +03:00
|
|
|
var (
|
|
|
|
selects, omits []string
|
2022-03-20 04:02:45 +03:00
|
|
|
onConflict = onConflictOption(db.Statement, rel.FieldSchema, defaultUpdatingColumns)
|
2020-11-18 14:06:49 +03:00
|
|
|
refName = rel.Name + "."
|
2022-03-17 18:53:31 +03:00
|
|
|
values = rValues.Interface()
|
2020-11-18 14:06:49 +03:00
|
|
|
)
|
2020-11-17 16:49:40 +03:00
|
|
|
|
|
|
|
for name, ok := range selectColumns {
|
|
|
|
columnName := ""
|
|
|
|
if strings.HasPrefix(name, refName) {
|
|
|
|
columnName = strings.TrimPrefix(name, refName)
|
|
|
|
}
|
|
|
|
|
|
|
|
if columnName != "" {
|
|
|
|
if ok {
|
|
|
|
selects = append(selects, columnName)
|
|
|
|
} else {
|
|
|
|
omits = append(omits, columnName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-18 08:25:52 +03:00
|
|
|
tx := db.Session(&gorm.Session{NewDB: true}).Clauses(onConflict).Session(&gorm.Session{
|
2021-02-26 12:11:25 +03:00
|
|
|
FullSaveAssociations: db.FullSaveAssociations,
|
2020-12-18 08:25:52 +03:00
|
|
|
SkipHooks: db.Statement.SkipHooks,
|
|
|
|
DisableNestedTransaction: true,
|
|
|
|
})
|
2020-11-17 16:49:40 +03:00
|
|
|
|
2021-01-05 16:12:31 +03:00
|
|
|
db.Statement.Settings.Range(func(k, v interface{}) bool {
|
|
|
|
tx.Statement.Settings.Store(k, v)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
2021-02-26 12:11:25 +03:00
|
|
|
if tx.Statement.FullSaveAssociations {
|
2021-06-18 10:38:20 +03:00
|
|
|
tx = tx.Set("gorm:update_track_time", true)
|
2021-02-26 12:11:25 +03:00
|
|
|
}
|
|
|
|
|
2020-11-17 16:49:40 +03:00
|
|
|
if len(selects) > 0 {
|
|
|
|
tx = tx.Select(selects)
|
2021-03-19 10:15:26 +03:00
|
|
|
} else if restricted && len(omits) == 0 {
|
2021-02-23 14:35:20 +03:00
|
|
|
tx = tx.Omit(clause.Associations)
|
2020-11-17 16:49:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(omits) > 0 {
|
|
|
|
tx = tx.Omit(omits...)
|
|
|
|
}
|
|
|
|
|
|
|
|
return db.AddError(tx.Create(values).Error)
|
|
|
|
}
|
2022-03-17 18:53:31 +03:00
|
|
|
|
|
|
|
// check association values has been saved
|
|
|
|
// if values kind is Struct, check it has been saved
|
|
|
|
// if values kind is Slice/Array, check all items have been saved
|
|
|
|
var visitMapStoreKey = "gorm:saved_association_map"
|
|
|
|
|
|
|
|
func checkAssociationsSaved(db *gorm.DB, values reflect.Value) bool {
|
|
|
|
if visit, ok := db.Get(visitMapStoreKey); ok {
|
|
|
|
if v, ok := visit.(*visitMap); ok {
|
|
|
|
if loadOrStoreVisitMap(v, values) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
vistMap := make(visitMap)
|
|
|
|
loadOrStoreVisitMap(&vistMap, values)
|
|
|
|
db.Set(visitMapStoreKey, &vistMap)
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|