Add serializer support (#5078)
* Update context * Update GormFieldValuer * Add Serializer * Add Serializer Interface * Refactor gorm field * Refactor setter, valuer * Add sync.Pool * Fix test * Add pool manager * Fix pool manager * Add poolInitializer * Add Serializer Scan support * Add Serializer Value method * Add serializer test * Finish Serializer * Fix JSONSerializer for postgres * Fix JSONSerializer for sqlserver * Test serializer tag * Add unixtime serializer * Update go.mod
This commit is contained in:
		
							parent
							
								
									19ac396a22
								
							
						
					
					
						commit
						39d84cba5f
					
				| @ -79,10 +79,10 @@ func (association *Association) Replace(values ...interface{}) error { | ||||
| 				switch reflectValue.Kind() { | ||||
| 				case reflect.Slice, reflect.Array: | ||||
| 					for i := 0; i < reflectValue.Len(); i++ { | ||||
| 						association.Error = rel.Field.Set(reflectValue.Index(i), reflect.Zero(rel.Field.FieldType).Interface()) | ||||
| 						association.Error = rel.Field.Set(association.DB.Statement.Context, reflectValue.Index(i), reflect.Zero(rel.Field.FieldType).Interface()) | ||||
| 					} | ||||
| 				case reflect.Struct: | ||||
| 					association.Error = rel.Field.Set(reflectValue, reflect.Zero(rel.Field.FieldType).Interface()) | ||||
| 					association.Error = rel.Field.Set(association.DB.Statement.Context, reflectValue, reflect.Zero(rel.Field.FieldType).Interface()) | ||||
| 				} | ||||
| 
 | ||||
| 				for _, ref := range rel.References { | ||||
| @ -96,12 +96,12 @@ func (association *Association) Replace(values ...interface{}) error { | ||||
| 				primaryFields []*schema.Field | ||||
| 				foreignKeys   []string | ||||
| 				updateMap     = map[string]interface{}{} | ||||
| 				relValues     = schema.GetRelationsValues(reflectValue, []*schema.Relationship{rel}) | ||||
| 				relValues     = schema.GetRelationsValues(association.DB.Statement.Context, reflectValue, []*schema.Relationship{rel}) | ||||
| 				modelValue    = reflect.New(rel.FieldSchema.ModelType).Interface() | ||||
| 				tx            = association.DB.Model(modelValue) | ||||
| 			) | ||||
| 
 | ||||
| 			if _, rvs := schema.GetIdentityFieldValuesMap(relValues, rel.FieldSchema.PrimaryFields); len(rvs) > 0 { | ||||
| 			if _, rvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, relValues, rel.FieldSchema.PrimaryFields); len(rvs) > 0 { | ||||
| 				if column, values := schema.ToQueryValues(rel.FieldSchema.Table, rel.FieldSchema.PrimaryFieldDBNames, rvs); len(values) > 0 { | ||||
| 					tx.Not(clause.IN{Column: column, Values: values}) | ||||
| 				} | ||||
| @ -117,7 +117,7 @@ func (association *Association) Replace(values ...interface{}) error { | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if _, pvs := schema.GetIdentityFieldValuesMap(reflectValue, primaryFields); len(pvs) > 0 { | ||||
| 			if _, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, primaryFields); len(pvs) > 0 { | ||||
| 				column, values := schema.ToQueryValues(rel.FieldSchema.Table, foreignKeys, pvs) | ||||
| 				association.Error = tx.Where(clause.IN{Column: column, Values: values}).UpdateColumns(updateMap).Error | ||||
| 			} | ||||
| @ -143,14 +143,14 @@ func (association *Association) Replace(values ...interface{}) error { | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			_, pvs := schema.GetIdentityFieldValuesMap(reflectValue, primaryFields) | ||||
| 			_, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, primaryFields) | ||||
| 			if column, values := schema.ToQueryValues(rel.JoinTable.Table, joinPrimaryKeys, pvs); len(values) > 0 { | ||||
| 				tx.Where(clause.IN{Column: column, Values: values}) | ||||
| 			} else { | ||||
| 				return ErrPrimaryKeyRequired | ||||
| 			} | ||||
| 
 | ||||
| 			_, rvs := schema.GetIdentityFieldValuesMapFromValues(values, relPrimaryFields) | ||||
| 			_, rvs := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, relPrimaryFields) | ||||
| 			if relColumn, relValues := schema.ToQueryValues(rel.JoinTable.Table, joinRelPrimaryKeys, rvs); len(relValues) > 0 { | ||||
| 				tx.Where(clause.Not(clause.IN{Column: relColumn, Values: relValues})) | ||||
| 			} | ||||
| @ -186,11 +186,11 @@ func (association *Association) Delete(values ...interface{}) error { | ||||
| 		case schema.BelongsTo: | ||||
| 			tx := association.DB.Model(reflect.New(rel.Schema.ModelType).Interface()) | ||||
| 
 | ||||
| 			_, pvs := schema.GetIdentityFieldValuesMap(reflectValue, rel.Schema.PrimaryFields) | ||||
| 			_, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, rel.Schema.PrimaryFields) | ||||
| 			pcolumn, pvalues := schema.ToQueryValues(rel.Schema.Table, rel.Schema.PrimaryFieldDBNames, pvs) | ||||
| 			conds = append(conds, clause.IN{Column: pcolumn, Values: pvalues}) | ||||
| 
 | ||||
| 			_, rvs := schema.GetIdentityFieldValuesMapFromValues(values, primaryFields) | ||||
| 			_, rvs := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, primaryFields) | ||||
| 			relColumn, relValues := schema.ToQueryValues(rel.Schema.Table, foreignKeys, rvs) | ||||
| 			conds = append(conds, clause.IN{Column: relColumn, Values: relValues}) | ||||
| 
 | ||||
| @ -198,11 +198,11 @@ func (association *Association) Delete(values ...interface{}) error { | ||||
| 		case schema.HasOne, schema.HasMany: | ||||
| 			tx := association.DB.Model(reflect.New(rel.FieldSchema.ModelType).Interface()) | ||||
| 
 | ||||
| 			_, pvs := schema.GetIdentityFieldValuesMap(reflectValue, primaryFields) | ||||
| 			_, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, primaryFields) | ||||
| 			pcolumn, pvalues := schema.ToQueryValues(rel.FieldSchema.Table, foreignKeys, pvs) | ||||
| 			conds = append(conds, clause.IN{Column: pcolumn, Values: pvalues}) | ||||
| 
 | ||||
| 			_, rvs := schema.GetIdentityFieldValuesMapFromValues(values, rel.FieldSchema.PrimaryFields) | ||||
| 			_, rvs := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, rel.FieldSchema.PrimaryFields) | ||||
| 			relColumn, relValues := schema.ToQueryValues(rel.FieldSchema.Table, rel.FieldSchema.PrimaryFieldDBNames, rvs) | ||||
| 			conds = append(conds, clause.IN{Column: relColumn, Values: relValues}) | ||||
| 
 | ||||
| @ -228,11 +228,11 @@ func (association *Association) Delete(values ...interface{}) error { | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			_, pvs := schema.GetIdentityFieldValuesMap(reflectValue, primaryFields) | ||||
| 			_, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, primaryFields) | ||||
| 			pcolumn, pvalues := schema.ToQueryValues(rel.JoinTable.Table, joinPrimaryKeys, pvs) | ||||
| 			conds = append(conds, clause.IN{Column: pcolumn, Values: pvalues}) | ||||
| 
 | ||||
| 			_, rvs := schema.GetIdentityFieldValuesMapFromValues(values, relPrimaryFields) | ||||
| 			_, rvs := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, relPrimaryFields) | ||||
| 			relColumn, relValues := schema.ToQueryValues(rel.JoinTable.Table, joinRelPrimaryKeys, rvs) | ||||
| 			conds = append(conds, clause.IN{Column: relColumn, Values: relValues}) | ||||
| 
 | ||||
| @ -241,11 +241,11 @@ func (association *Association) Delete(values ...interface{}) error { | ||||
| 
 | ||||
| 		if association.Error == nil { | ||||
| 			// clean up deleted values's foreign key
 | ||||
| 			relValuesMap, _ := schema.GetIdentityFieldValuesMapFromValues(values, rel.FieldSchema.PrimaryFields) | ||||
| 			relValuesMap, _ := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, rel.FieldSchema.PrimaryFields) | ||||
| 
 | ||||
| 			cleanUpDeletedRelations := func(data reflect.Value) { | ||||
| 				if _, zero := rel.Field.ValueOf(data); !zero { | ||||
| 					fieldValue := reflect.Indirect(rel.Field.ReflectValueOf(data)) | ||||
| 				if _, zero := rel.Field.ValueOf(association.DB.Statement.Context, data); !zero { | ||||
| 					fieldValue := reflect.Indirect(rel.Field.ReflectValueOf(association.DB.Statement.Context, data)) | ||||
| 					primaryValues := make([]interface{}, len(rel.FieldSchema.PrimaryFields)) | ||||
| 
 | ||||
| 					switch fieldValue.Kind() { | ||||
| @ -253,7 +253,7 @@ func (association *Association) Delete(values ...interface{}) error { | ||||
| 						validFieldValues := reflect.Zero(rel.Field.IndirectFieldType) | ||||
| 						for i := 0; i < fieldValue.Len(); i++ { | ||||
| 							for idx, field := range rel.FieldSchema.PrimaryFields { | ||||
| 								primaryValues[idx], _ = field.ValueOf(fieldValue.Index(i)) | ||||
| 								primaryValues[idx], _ = field.ValueOf(association.DB.Statement.Context, fieldValue.Index(i)) | ||||
| 							} | ||||
| 
 | ||||
| 							if _, ok := relValuesMap[utils.ToStringKey(primaryValues...)]; !ok { | ||||
| @ -261,23 +261,23 @@ func (association *Association) Delete(values ...interface{}) error { | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| 						association.Error = rel.Field.Set(data, validFieldValues.Interface()) | ||||
| 						association.Error = rel.Field.Set(association.DB.Statement.Context, data, validFieldValues.Interface()) | ||||
| 					case reflect.Struct: | ||||
| 						for idx, field := range rel.FieldSchema.PrimaryFields { | ||||
| 							primaryValues[idx], _ = field.ValueOf(fieldValue) | ||||
| 							primaryValues[idx], _ = field.ValueOf(association.DB.Statement.Context, fieldValue) | ||||
| 						} | ||||
| 
 | ||||
| 						if _, ok := relValuesMap[utils.ToStringKey(primaryValues...)]; ok { | ||||
| 							if association.Error = rel.Field.Set(data, reflect.Zero(rel.FieldSchema.ModelType).Interface()); association.Error != nil { | ||||
| 							if association.Error = rel.Field.Set(association.DB.Statement.Context, data, reflect.Zero(rel.FieldSchema.ModelType).Interface()); association.Error != nil { | ||||
| 								break | ||||
| 							} | ||||
| 
 | ||||
| 							if rel.JoinTable == nil { | ||||
| 								for _, ref := range rel.References { | ||||
| 									if ref.OwnPrimaryKey || ref.PrimaryValue != "" { | ||||
| 										association.Error = ref.ForeignKey.Set(fieldValue, reflect.Zero(ref.ForeignKey.FieldType).Interface()) | ||||
| 										association.Error = ref.ForeignKey.Set(association.DB.Statement.Context, fieldValue, reflect.Zero(ref.ForeignKey.FieldType).Interface()) | ||||
| 									} else { | ||||
| 										association.Error = ref.ForeignKey.Set(data, reflect.Zero(ref.ForeignKey.FieldType).Interface()) | ||||
| 										association.Error = ref.ForeignKey.Set(association.DB.Statement.Context, data, reflect.Zero(ref.ForeignKey.FieldType).Interface()) | ||||
| 									} | ||||
| 								} | ||||
| 							} | ||||
| @ -329,14 +329,14 @@ func (association *Association) saveAssociation(clear bool, values ...interface{ | ||||
| 			switch rv.Kind() { | ||||
| 			case reflect.Slice, reflect.Array: | ||||
| 				if rv.Len() > 0 { | ||||
| 					association.Error = association.Relationship.Field.Set(source, rv.Index(0).Addr().Interface()) | ||||
| 					association.Error = association.Relationship.Field.Set(association.DB.Statement.Context, source, rv.Index(0).Addr().Interface()) | ||||
| 
 | ||||
| 					if association.Relationship.Field.FieldType.Kind() == reflect.Struct { | ||||
| 						assignBacks = append(assignBacks, assignBack{Source: source, Dest: rv.Index(0)}) | ||||
| 					} | ||||
| 				} | ||||
| 			case reflect.Struct: | ||||
| 				association.Error = association.Relationship.Field.Set(source, rv.Addr().Interface()) | ||||
| 				association.Error = association.Relationship.Field.Set(association.DB.Statement.Context, source, rv.Addr().Interface()) | ||||
| 
 | ||||
| 				if association.Relationship.Field.FieldType.Kind() == reflect.Struct { | ||||
| 					assignBacks = append(assignBacks, assignBack{Source: source, Dest: rv}) | ||||
| @ -344,7 +344,7 @@ func (association *Association) saveAssociation(clear bool, values ...interface{ | ||||
| 			} | ||||
| 		case schema.HasMany, schema.Many2Many: | ||||
| 			elemType := association.Relationship.Field.IndirectFieldType.Elem() | ||||
| 			fieldValue := reflect.Indirect(association.Relationship.Field.ReflectValueOf(source)) | ||||
| 			fieldValue := reflect.Indirect(association.Relationship.Field.ReflectValueOf(association.DB.Statement.Context, source)) | ||||
| 			if clear { | ||||
| 				fieldValue = reflect.New(association.Relationship.Field.IndirectFieldType).Elem() | ||||
| 			} | ||||
| @ -373,7 +373,7 @@ func (association *Association) saveAssociation(clear bool, values ...interface{ | ||||
| 			} | ||||
| 
 | ||||
| 			if association.Error == nil { | ||||
| 				association.Error = association.Relationship.Field.Set(source, fieldValue.Interface()) | ||||
| 				association.Error = association.Relationship.Field.Set(association.DB.Statement.Context, source, fieldValue.Interface()) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @ -421,7 +421,7 @@ func (association *Association) saveAssociation(clear bool, values ...interface{ | ||||
| 			// clear old data
 | ||||
| 			if clear && len(values) == 0 { | ||||
| 				for i := 0; i < reflectValue.Len(); i++ { | ||||
| 					if err := association.Relationship.Field.Set(reflectValue.Index(i), reflect.New(association.Relationship.Field.IndirectFieldType).Interface()); err != nil { | ||||
| 					if err := association.Relationship.Field.Set(association.DB.Statement.Context, reflectValue.Index(i), reflect.New(association.Relationship.Field.IndirectFieldType).Interface()); err != nil { | ||||
| 						association.Error = err | ||||
| 						break | ||||
| 					} | ||||
| @ -429,7 +429,7 @@ func (association *Association) saveAssociation(clear bool, values ...interface{ | ||||
| 					if association.Relationship.JoinTable == nil { | ||||
| 						for _, ref := range association.Relationship.References { | ||||
| 							if !ref.OwnPrimaryKey && ref.PrimaryValue == "" { | ||||
| 								if err := ref.ForeignKey.Set(reflectValue.Index(i), reflect.Zero(ref.ForeignKey.FieldType).Interface()); err != nil { | ||||
| 								if err := ref.ForeignKey.Set(association.DB.Statement.Context, reflectValue.Index(i), reflect.Zero(ref.ForeignKey.FieldType).Interface()); err != nil { | ||||
| 									association.Error = err | ||||
| 									break | ||||
| 								} | ||||
| @ -453,12 +453,12 @@ func (association *Association) saveAssociation(clear bool, values ...interface{ | ||||
| 	case reflect.Struct: | ||||
| 		// clear old data
 | ||||
| 		if clear && len(values) == 0 { | ||||
| 			association.Error = association.Relationship.Field.Set(reflectValue, reflect.New(association.Relationship.Field.IndirectFieldType).Interface()) | ||||
| 			association.Error = association.Relationship.Field.Set(association.DB.Statement.Context, reflectValue, reflect.New(association.Relationship.Field.IndirectFieldType).Interface()) | ||||
| 
 | ||||
| 			if association.Relationship.JoinTable == nil && association.Error == nil { | ||||
| 				for _, ref := range association.Relationship.References { | ||||
| 					if !ref.OwnPrimaryKey && ref.PrimaryValue == "" { | ||||
| 						association.Error = ref.ForeignKey.Set(reflectValue, reflect.Zero(ref.ForeignKey.FieldType).Interface()) | ||||
| 						association.Error = ref.ForeignKey.Set(association.DB.Statement.Context, reflectValue, reflect.Zero(ref.ForeignKey.FieldType).Interface()) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| @ -475,7 +475,7 @@ func (association *Association) saveAssociation(clear bool, values ...interface{ | ||||
| 	} | ||||
| 
 | ||||
| 	for _, assignBack := range assignBacks { | ||||
| 		fieldValue := reflect.Indirect(association.Relationship.Field.ReflectValueOf(assignBack.Source)) | ||||
| 		fieldValue := reflect.Indirect(association.Relationship.Field.ReflectValueOf(association.DB.Statement.Context, assignBack.Source)) | ||||
| 		if assignBack.Index > 0 { | ||||
| 			reflect.Indirect(assignBack.Dest).Set(fieldValue.Index(assignBack.Index - 1)) | ||||
| 		} else { | ||||
| @ -486,7 +486,7 @@ func (association *Association) saveAssociation(clear bool, values ...interface{ | ||||
| 
 | ||||
| func (association *Association) buildCondition() *DB { | ||||
| 	var ( | ||||
| 		queryConds = association.Relationship.ToQueryConditions(association.DB.Statement.ReflectValue) | ||||
| 		queryConds = association.Relationship.ToQueryConditions(association.DB.Statement.Context, association.DB.Statement.ReflectValue) | ||||
| 		modelValue = reflect.New(association.Relationship.FieldSchema.ModelType).Interface() | ||||
| 		tx         = association.DB.Model(modelValue) | ||||
| 	) | ||||
|  | ||||
| @ -24,8 +24,8 @@ func SaveBeforeAssociations(create bool) func(db *gorm.DB) { | ||||
| 				setupReferences := func(obj reflect.Value, elem reflect.Value) { | ||||
| 					for _, ref := range rel.References { | ||||
| 						if !ref.OwnPrimaryKey { | ||||
| 							pv, _ := ref.PrimaryKey.ValueOf(elem) | ||||
| 							db.AddError(ref.ForeignKey.Set(obj, pv)) | ||||
| 							pv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, elem) | ||||
| 							db.AddError(ref.ForeignKey.Set(db.Statement.Context, obj, pv)) | ||||
| 
 | ||||
| 							if dest, ok := db.Statement.Dest.(map[string]interface{}); ok { | ||||
| 								dest[ref.ForeignKey.DBName] = pv | ||||
| @ -57,8 +57,8 @@ func SaveBeforeAssociations(create bool) func(db *gorm.DB) { | ||||
| 							break | ||||
| 						} | ||||
| 
 | ||||
| 						if _, zero := rel.Field.ValueOf(obj); !zero { // check belongs to relation value
 | ||||
| 							rv := rel.Field.ReflectValueOf(obj) // relation reflect value
 | ||||
| 						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
 | ||||
| 							objs = append(objs, obj) | ||||
| 							if isPtr { | ||||
| 								elems = reflect.Append(elems, rv) | ||||
| @ -76,8 +76,8 @@ func SaveBeforeAssociations(create bool) func(db *gorm.DB) { | ||||
| 						} | ||||
| 					} | ||||
| 				case reflect.Struct: | ||||
| 					if _, zero := rel.Field.ValueOf(db.Statement.ReflectValue); !zero { | ||||
| 						rv := rel.Field.ReflectValueOf(db.Statement.ReflectValue) // relation reflect value
 | ||||
| 					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
 | ||||
| 						if rv.Kind() != reflect.Ptr { | ||||
| 							rv = rv.Addr() | ||||
| 						} | ||||
| @ -120,18 +120,18 @@ func SaveAfterAssociations(create bool) func(db *gorm.DB) { | ||||
| 						obj := db.Statement.ReflectValue.Index(i) | ||||
| 
 | ||||
| 						if reflect.Indirect(obj).Kind() == reflect.Struct { | ||||
| 							if _, zero := rel.Field.ValueOf(obj); !zero { | ||||
| 								rv := rel.Field.ReflectValueOf(obj) | ||||
| 							if _, zero := rel.Field.ValueOf(db.Statement.Context, obj); !zero { | ||||
| 								rv := rel.Field.ReflectValueOf(db.Statement.Context, obj) | ||||
| 								if rv.Kind() != reflect.Ptr { | ||||
| 									rv = rv.Addr() | ||||
| 								} | ||||
| 
 | ||||
| 								for _, ref := range rel.References { | ||||
| 									if ref.OwnPrimaryKey { | ||||
| 										fv, _ := ref.PrimaryKey.ValueOf(obj) | ||||
| 										db.AddError(ref.ForeignKey.Set(rv, fv)) | ||||
| 										fv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, obj) | ||||
| 										db.AddError(ref.ForeignKey.Set(db.Statement.Context, rv, fv)) | ||||
| 									} else if ref.PrimaryValue != "" { | ||||
| 										db.AddError(ref.ForeignKey.Set(rv, ref.PrimaryValue)) | ||||
| 										db.AddError(ref.ForeignKey.Set(db.Statement.Context, rv, ref.PrimaryValue)) | ||||
| 									} | ||||
| 								} | ||||
| 
 | ||||
| @ -149,8 +149,8 @@ func SaveAfterAssociations(create bool) func(db *gorm.DB) { | ||||
| 						saveAssociations(db, rel, elems.Interface(), selectColumns, restricted, assignmentColumns) | ||||
| 					} | ||||
| 				case reflect.Struct: | ||||
| 					if _, zero := rel.Field.ValueOf(db.Statement.ReflectValue); !zero { | ||||
| 						f := rel.Field.ReflectValueOf(db.Statement.ReflectValue) | ||||
| 					if _, zero := rel.Field.ValueOf(db.Statement.Context, db.Statement.ReflectValue); !zero { | ||||
| 						f := rel.Field.ReflectValueOf(db.Statement.Context, db.Statement.ReflectValue) | ||||
| 						if f.Kind() != reflect.Ptr { | ||||
| 							f = f.Addr() | ||||
| 						} | ||||
| @ -158,10 +158,10 @@ func SaveAfterAssociations(create bool) func(db *gorm.DB) { | ||||
| 						assignmentColumns := make([]string, 0, len(rel.References)) | ||||
| 						for _, ref := range rel.References { | ||||
| 							if ref.OwnPrimaryKey { | ||||
| 								fv, _ := ref.PrimaryKey.ValueOf(db.Statement.ReflectValue) | ||||
| 								ref.ForeignKey.Set(f, fv) | ||||
| 								fv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, db.Statement.ReflectValue) | ||||
| 								ref.ForeignKey.Set(db.Statement.Context, f, fv) | ||||
| 							} else if ref.PrimaryValue != "" { | ||||
| 								ref.ForeignKey.Set(f, ref.PrimaryValue) | ||||
| 								ref.ForeignKey.Set(db.Statement.Context, f, ref.PrimaryValue) | ||||
| 							} | ||||
| 							assignmentColumns = append(assignmentColumns, ref.ForeignKey.DBName) | ||||
| 						} | ||||
| @ -185,23 +185,23 @@ func SaveAfterAssociations(create bool) func(db *gorm.DB) { | ||||
| 				elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10) | ||||
| 				identityMap := map[string]bool{} | ||||
| 				appendToElems := func(v reflect.Value) { | ||||
| 					if _, zero := rel.Field.ValueOf(v); !zero { | ||||
| 						f := reflect.Indirect(rel.Field.ReflectValueOf(v)) | ||||
| 					if _, zero := rel.Field.ValueOf(db.Statement.Context, v); !zero { | ||||
| 						f := reflect.Indirect(rel.Field.ReflectValueOf(db.Statement.Context, v)) | ||||
| 
 | ||||
| 						for i := 0; i < f.Len(); i++ { | ||||
| 							elem := f.Index(i) | ||||
| 							for _, ref := range rel.References { | ||||
| 								if ref.OwnPrimaryKey { | ||||
| 									pv, _ := ref.PrimaryKey.ValueOf(v) | ||||
| 									ref.ForeignKey.Set(elem, pv) | ||||
| 									pv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, v) | ||||
| 									ref.ForeignKey.Set(db.Statement.Context, elem, pv) | ||||
| 								} else if ref.PrimaryValue != "" { | ||||
| 									ref.ForeignKey.Set(elem, ref.PrimaryValue) | ||||
| 									ref.ForeignKey.Set(db.Statement.Context, elem, ref.PrimaryValue) | ||||
| 								} | ||||
| 							} | ||||
| 
 | ||||
| 							relPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields)) | ||||
| 							for _, pf := range rel.FieldSchema.PrimaryFields { | ||||
| 								if pfv, ok := pf.ValueOf(elem); !ok { | ||||
| 								if pfv, ok := pf.ValueOf(db.Statement.Context, elem); !ok { | ||||
| 									relPrimaryValues = append(relPrimaryValues, pfv) | ||||
| 								} | ||||
| 							} | ||||
| @ -260,21 +260,21 @@ func SaveAfterAssociations(create bool) func(db *gorm.DB) { | ||||
| 					joinValue := reflect.New(rel.JoinTable.ModelType) | ||||
| 					for _, ref := range rel.References { | ||||
| 						if ref.OwnPrimaryKey { | ||||
| 							fv, _ := ref.PrimaryKey.ValueOf(obj) | ||||
| 							ref.ForeignKey.Set(joinValue, fv) | ||||
| 							fv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, obj) | ||||
| 							ref.ForeignKey.Set(db.Statement.Context, joinValue, fv) | ||||
| 						} else if ref.PrimaryValue != "" { | ||||
| 							ref.ForeignKey.Set(joinValue, ref.PrimaryValue) | ||||
| 							ref.ForeignKey.Set(db.Statement.Context, joinValue, ref.PrimaryValue) | ||||
| 						} else { | ||||
| 							fv, _ := ref.PrimaryKey.ValueOf(elem) | ||||
| 							ref.ForeignKey.Set(joinValue, fv) | ||||
| 							fv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, elem) | ||||
| 							ref.ForeignKey.Set(db.Statement.Context, joinValue, fv) | ||||
| 						} | ||||
| 					} | ||||
| 					joins = reflect.Append(joins, joinValue) | ||||
| 				} | ||||
| 
 | ||||
| 				appendToElems := func(v reflect.Value) { | ||||
| 					if _, zero := rel.Field.ValueOf(v); !zero { | ||||
| 						f := reflect.Indirect(rel.Field.ReflectValueOf(v)) | ||||
| 					if _, zero := rel.Field.ValueOf(db.Statement.Context, v); !zero { | ||||
| 						f := reflect.Indirect(rel.Field.ReflectValueOf(db.Statement.Context, v)) | ||||
| 
 | ||||
| 						for i := 0; i < f.Len(); i++ { | ||||
| 							elem := f.Index(i) | ||||
|  | ||||
| @ -117,9 +117,9 @@ func Create(config *Config) func(db *gorm.DB) { | ||||
| 							break | ||||
| 						} | ||||
| 
 | ||||
| 						_, isZero := db.Statement.Schema.PrioritizedPrimaryField.ValueOf(rv) | ||||
| 						_, isZero := db.Statement.Schema.PrioritizedPrimaryField.ValueOf(db.Statement.Context, rv) | ||||
| 						if isZero { | ||||
| 							db.Statement.Schema.PrioritizedPrimaryField.Set(rv, insertID) | ||||
| 							db.Statement.Schema.PrioritizedPrimaryField.Set(db.Statement.Context, rv, insertID) | ||||
| 							insertID -= db.Statement.Schema.PrioritizedPrimaryField.AutoIncrementIncrement | ||||
| 						} | ||||
| 					} | ||||
| @ -130,16 +130,16 @@ func Create(config *Config) func(db *gorm.DB) { | ||||
| 							break | ||||
| 						} | ||||
| 
 | ||||
| 						if _, isZero := db.Statement.Schema.PrioritizedPrimaryField.ValueOf(rv); isZero { | ||||
| 							db.Statement.Schema.PrioritizedPrimaryField.Set(rv, insertID) | ||||
| 						if _, isZero := db.Statement.Schema.PrioritizedPrimaryField.ValueOf(db.Statement.Context, rv); isZero { | ||||
| 							db.Statement.Schema.PrioritizedPrimaryField.Set(db.Statement.Context, rv, insertID) | ||||
| 							insertID += db.Statement.Schema.PrioritizedPrimaryField.AutoIncrementIncrement | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			case reflect.Struct: | ||||
| 				_, isZero := db.Statement.Schema.PrioritizedPrimaryField.ValueOf(db.Statement.ReflectValue) | ||||
| 				_, isZero := db.Statement.Schema.PrioritizedPrimaryField.ValueOf(db.Statement.Context, db.Statement.ReflectValue) | ||||
| 				if isZero { | ||||
| 					db.Statement.Schema.PrioritizedPrimaryField.Set(db.Statement.ReflectValue, insertID) | ||||
| 					db.Statement.Schema.PrioritizedPrimaryField.Set(db.Statement.Context, db.Statement.ReflectValue, insertID) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @ -219,23 +219,23 @@ func ConvertToCreateValues(stmt *gorm.Statement) (values clause.Values) { | ||||
| 				values.Values[i] = make([]interface{}, len(values.Columns)) | ||||
| 				for idx, column := range values.Columns { | ||||
| 					field := stmt.Schema.FieldsByDBName[column.Name] | ||||
| 					if values.Values[i][idx], isZero = field.ValueOf(rv); isZero { | ||||
| 					if values.Values[i][idx], isZero = field.ValueOf(stmt.Context, rv); isZero { | ||||
| 						if field.DefaultValueInterface != nil { | ||||
| 							values.Values[i][idx] = field.DefaultValueInterface | ||||
| 							field.Set(rv, field.DefaultValueInterface) | ||||
| 							field.Set(stmt.Context, rv, field.DefaultValueInterface) | ||||
| 						} else if field.AutoCreateTime > 0 || field.AutoUpdateTime > 0 { | ||||
| 							field.Set(rv, curTime) | ||||
| 							values.Values[i][idx], _ = field.ValueOf(rv) | ||||
| 							field.Set(stmt.Context, rv, curTime) | ||||
| 							values.Values[i][idx], _ = field.ValueOf(stmt.Context, rv) | ||||
| 						} | ||||
| 					} else if field.AutoUpdateTime > 0 && updateTrackTime { | ||||
| 						field.Set(rv, curTime) | ||||
| 						values.Values[i][idx], _ = field.ValueOf(rv) | ||||
| 						field.Set(stmt.Context, rv, curTime) | ||||
| 						values.Values[i][idx], _ = field.ValueOf(stmt.Context, rv) | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				for _, field := range stmt.Schema.FieldsWithDefaultDBValue { | ||||
| 					if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) { | ||||
| 						if rvOfvalue, isZero := field.ValueOf(rv); !isZero { | ||||
| 						if rvOfvalue, isZero := field.ValueOf(stmt.Context, rv); !isZero { | ||||
| 							if len(defaultValueFieldsHavingValue[field]) == 0 { | ||||
| 								defaultValueFieldsHavingValue[field] = make([]interface{}, rValLen) | ||||
| 							} | ||||
| @ -259,23 +259,23 @@ func ConvertToCreateValues(stmt *gorm.Statement) (values clause.Values) { | ||||
| 			values.Values = [][]interface{}{make([]interface{}, len(values.Columns))} | ||||
| 			for idx, column := range values.Columns { | ||||
| 				field := stmt.Schema.FieldsByDBName[column.Name] | ||||
| 				if values.Values[0][idx], isZero = field.ValueOf(stmt.ReflectValue); isZero { | ||||
| 				if values.Values[0][idx], isZero = field.ValueOf(stmt.Context, stmt.ReflectValue); isZero { | ||||
| 					if field.DefaultValueInterface != nil { | ||||
| 						values.Values[0][idx] = field.DefaultValueInterface | ||||
| 						field.Set(stmt.ReflectValue, field.DefaultValueInterface) | ||||
| 						field.Set(stmt.Context, stmt.ReflectValue, field.DefaultValueInterface) | ||||
| 					} else if field.AutoCreateTime > 0 || field.AutoUpdateTime > 0 { | ||||
| 						field.Set(stmt.ReflectValue, curTime) | ||||
| 						values.Values[0][idx], _ = field.ValueOf(stmt.ReflectValue) | ||||
| 						field.Set(stmt.Context, stmt.ReflectValue, curTime) | ||||
| 						values.Values[0][idx], _ = field.ValueOf(stmt.Context, stmt.ReflectValue) | ||||
| 					} | ||||
| 				} else if field.AutoUpdateTime > 0 && updateTrackTime { | ||||
| 					field.Set(stmt.ReflectValue, curTime) | ||||
| 					values.Values[0][idx], _ = field.ValueOf(stmt.ReflectValue) | ||||
| 					field.Set(stmt.Context, stmt.ReflectValue, curTime) | ||||
| 					values.Values[0][idx], _ = field.ValueOf(stmt.Context, stmt.ReflectValue) | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			for _, field := range stmt.Schema.FieldsWithDefaultDBValue { | ||||
| 				if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) { | ||||
| 					if rvOfvalue, isZero := field.ValueOf(stmt.ReflectValue); !isZero { | ||||
| 					if rvOfvalue, isZero := field.ValueOf(stmt.Context, stmt.ReflectValue); !isZero { | ||||
| 						values.Columns = append(values.Columns, clause.Column{Name: field.DBName}) | ||||
| 						values.Values[0] = append(values.Values[0], rvOfvalue) | ||||
| 					} | ||||
|  | ||||
| @ -42,7 +42,7 @@ func DeleteBeforeAssociations(db *gorm.DB) { | ||||
| 
 | ||||
| 			switch rel.Type { | ||||
| 			case schema.HasOne, schema.HasMany: | ||||
| 				queryConds := rel.ToQueryConditions(db.Statement.ReflectValue) | ||||
| 				queryConds := rel.ToQueryConditions(db.Statement.Context, db.Statement.ReflectValue) | ||||
| 				modelValue := reflect.New(rel.FieldSchema.ModelType).Interface() | ||||
| 				tx := db.Session(&gorm.Session{NewDB: true}).Model(modelValue) | ||||
| 				withoutConditions := false | ||||
| @ -97,7 +97,7 @@ func DeleteBeforeAssociations(db *gorm.DB) { | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				_, foreignValues := schema.GetIdentityFieldValuesMap(db.Statement.ReflectValue, foreignFields) | ||||
| 				_, foreignValues := schema.GetIdentityFieldValuesMap(db.Statement.Context, db.Statement.ReflectValue, foreignFields) | ||||
| 				column, values := schema.ToQueryValues(table, relForeignKeys, foreignValues) | ||||
| 				queryConds = append(queryConds, clause.IN{Column: column, Values: values}) | ||||
| 
 | ||||
| @ -123,7 +123,7 @@ func Delete(config *Config) func(db *gorm.DB) { | ||||
| 			db.Statement.AddClauseIfNotExists(clause.Delete{}) | ||||
| 
 | ||||
| 			if db.Statement.Schema != nil { | ||||
| 				_, queryValues := schema.GetIdentityFieldValuesMap(db.Statement.ReflectValue, db.Statement.Schema.PrimaryFields) | ||||
| 				_, queryValues := schema.GetIdentityFieldValuesMap(db.Statement.Context, db.Statement.ReflectValue, db.Statement.Schema.PrimaryFields) | ||||
| 				column, values := schema.ToQueryValues(db.Statement.Table, db.Statement.Schema.PrimaryFieldDBNames, queryValues) | ||||
| 
 | ||||
| 				if len(values) > 0 { | ||||
| @ -131,7 +131,7 @@ func Delete(config *Config) func(db *gorm.DB) { | ||||
| 				} | ||||
| 
 | ||||
| 				if db.Statement.ReflectValue.CanAddr() && db.Statement.Dest != db.Statement.Model && db.Statement.Model != nil { | ||||
| 					_, queryValues = schema.GetIdentityFieldValuesMap(reflect.ValueOf(db.Statement.Model), db.Statement.Schema.PrimaryFields) | ||||
| 					_, queryValues = schema.GetIdentityFieldValuesMap(db.Statement.Context, reflect.ValueOf(db.Statement.Model), db.Statement.Schema.PrimaryFields) | ||||
| 					column, values = schema.ToQueryValues(db.Statement.Table, db.Statement.Schema.PrimaryFieldDBNames, queryValues) | ||||
| 
 | ||||
| 					if len(values) > 0 { | ||||
|  | ||||
| @ -48,7 +48,7 @@ func preload(db *gorm.DB, rel *schema.Relationship, conds []interface{}, preload | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		joinIdentityMap, joinForeignValues := schema.GetIdentityFieldValuesMap(reflectValue, foreignFields) | ||||
| 		joinIdentityMap, joinForeignValues := schema.GetIdentityFieldValuesMap(db.Statement.Context, reflectValue, foreignFields) | ||||
| 		if len(joinForeignValues) == 0 { | ||||
| 			return | ||||
| 		} | ||||
| @ -63,11 +63,11 @@ func preload(db *gorm.DB, rel *schema.Relationship, conds []interface{}, preload | ||||
| 		for i := 0; i < joinResults.Len(); i++ { | ||||
| 			joinIndexValue := joinResults.Index(i) | ||||
| 			for idx, field := range joinForeignFields { | ||||
| 				fieldValues[idx], _ = field.ValueOf(joinIndexValue) | ||||
| 				fieldValues[idx], _ = field.ValueOf(db.Statement.Context, joinIndexValue) | ||||
| 			} | ||||
| 
 | ||||
| 			for idx, field := range joinRelForeignFields { | ||||
| 				joinFieldValues[idx], _ = field.ValueOf(joinIndexValue) | ||||
| 				joinFieldValues[idx], _ = field.ValueOf(db.Statement.Context, joinIndexValue) | ||||
| 			} | ||||
| 
 | ||||
| 			if results, ok := joinIdentityMap[utils.ToStringKey(fieldValues...)]; ok { | ||||
| @ -76,7 +76,7 @@ func preload(db *gorm.DB, rel *schema.Relationship, conds []interface{}, preload | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		_, foreignValues = schema.GetIdentityFieldValuesMap(joinResults, joinRelForeignFields) | ||||
| 		_, foreignValues = schema.GetIdentityFieldValuesMap(db.Statement.Context, joinResults, joinRelForeignFields) | ||||
| 	} else { | ||||
| 		for _, ref := range rel.References { | ||||
| 			if ref.OwnPrimaryKey { | ||||
| @ -92,7 +92,7 @@ func preload(db *gorm.DB, rel *schema.Relationship, conds []interface{}, preload | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		identityMap, foreignValues = schema.GetIdentityFieldValuesMap(reflectValue, foreignFields) | ||||
| 		identityMap, foreignValues = schema.GetIdentityFieldValuesMap(db.Statement.Context, reflectValue, foreignFields) | ||||
| 		if len(foreignValues) == 0 { | ||||
| 			return | ||||
| 		} | ||||
| @ -125,17 +125,17 @@ func preload(db *gorm.DB, rel *schema.Relationship, conds []interface{}, preload | ||||
| 	case reflect.Struct: | ||||
| 		switch rel.Type { | ||||
| 		case schema.HasMany, schema.Many2Many: | ||||
| 			rel.Field.Set(reflectValue, reflect.MakeSlice(rel.Field.IndirectFieldType, 0, 10).Interface()) | ||||
| 			rel.Field.Set(db.Statement.Context, reflectValue, reflect.MakeSlice(rel.Field.IndirectFieldType, 0, 10).Interface()) | ||||
| 		default: | ||||
| 			rel.Field.Set(reflectValue, reflect.New(rel.Field.FieldType).Interface()) | ||||
| 			rel.Field.Set(db.Statement.Context, reflectValue, reflect.New(rel.Field.FieldType).Interface()) | ||||
| 		} | ||||
| 	case reflect.Slice, reflect.Array: | ||||
| 		for i := 0; i < reflectValue.Len(); i++ { | ||||
| 			switch rel.Type { | ||||
| 			case schema.HasMany, schema.Many2Many: | ||||
| 				rel.Field.Set(reflectValue.Index(i), reflect.MakeSlice(rel.Field.IndirectFieldType, 0, 10).Interface()) | ||||
| 				rel.Field.Set(db.Statement.Context, reflectValue.Index(i), reflect.MakeSlice(rel.Field.IndirectFieldType, 0, 10).Interface()) | ||||
| 			default: | ||||
| 				rel.Field.Set(reflectValue.Index(i), reflect.New(rel.Field.FieldType).Interface()) | ||||
| 				rel.Field.Set(db.Statement.Context, reflectValue.Index(i), reflect.New(rel.Field.FieldType).Interface()) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @ -143,7 +143,7 @@ func preload(db *gorm.DB, rel *schema.Relationship, conds []interface{}, preload | ||||
| 	for i := 0; i < reflectResults.Len(); i++ { | ||||
| 		elem := reflectResults.Index(i) | ||||
| 		for idx, field := range relForeignFields { | ||||
| 			fieldValues[idx], _ = field.ValueOf(elem) | ||||
| 			fieldValues[idx], _ = field.ValueOf(db.Statement.Context, elem) | ||||
| 		} | ||||
| 
 | ||||
| 		datas, ok := identityMap[utils.ToStringKey(fieldValues...)] | ||||
| @ -154,7 +154,7 @@ func preload(db *gorm.DB, rel *schema.Relationship, conds []interface{}, preload | ||||
| 		} | ||||
| 
 | ||||
| 		for _, data := range datas { | ||||
| 			reflectFieldValue := rel.Field.ReflectValueOf(data) | ||||
| 			reflectFieldValue := rel.Field.ReflectValueOf(db.Statement.Context, data) | ||||
| 			if reflectFieldValue.Kind() == reflect.Ptr && reflectFieldValue.IsNil() { | ||||
| 				reflectFieldValue.Set(reflect.New(rel.Field.FieldType.Elem())) | ||||
| 			} | ||||
| @ -162,12 +162,12 @@ func preload(db *gorm.DB, rel *schema.Relationship, conds []interface{}, preload | ||||
| 			reflectFieldValue = reflect.Indirect(reflectFieldValue) | ||||
| 			switch reflectFieldValue.Kind() { | ||||
| 			case reflect.Struct: | ||||
| 				rel.Field.Set(data, elem.Interface()) | ||||
| 				rel.Field.Set(db.Statement.Context, data, elem.Interface()) | ||||
| 			case reflect.Slice, reflect.Array: | ||||
| 				if reflectFieldValue.Type().Elem().Kind() == reflect.Ptr { | ||||
| 					rel.Field.Set(data, reflect.Append(reflectFieldValue, elem).Interface()) | ||||
| 					rel.Field.Set(db.Statement.Context, data, reflect.Append(reflectFieldValue, elem).Interface()) | ||||
| 				} else { | ||||
| 					rel.Field.Set(data, reflect.Append(reflectFieldValue, elem.Elem()).Interface()) | ||||
| 					rel.Field.Set(db.Statement.Context, data, reflect.Append(reflectFieldValue, elem.Elem()).Interface()) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @ -40,7 +40,7 @@ func BuildQuerySQL(db *gorm.DB) { | ||||
| 		if db.Statement.ReflectValue.Kind() == reflect.Struct && db.Statement.ReflectValue.Type() == db.Statement.Schema.ModelType { | ||||
| 			var conds []clause.Expression | ||||
| 			for _, primaryField := range db.Statement.Schema.PrimaryFields { | ||||
| 				if v, isZero := primaryField.ValueOf(db.Statement.ReflectValue); !isZero { | ||||
| 				if v, isZero := primaryField.ValueOf(db.Statement.Context, db.Statement.ReflectValue); !isZero { | ||||
| 					conds = append(conds, clause.Eq{Column: clause.Column{Table: db.Statement.Table, Name: primaryField.DBName}, Value: v}) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| @ -21,7 +21,7 @@ func SetupUpdateReflectValue(db *gorm.DB) { | ||||
| 			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]) | ||||
| 						rel.Field.Set(db.Statement.Context, db.Statement.ReflectValue, dest[rel.Name]) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| @ -137,13 +137,13 @@ func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) { | ||||
| 	case reflect.Slice, reflect.Array: | ||||
| 		assignValue = func(field *schema.Field, value interface{}) { | ||||
| 			for i := 0; i < stmt.ReflectValue.Len(); i++ { | ||||
| 				field.Set(stmt.ReflectValue.Index(i), value) | ||||
| 				field.Set(stmt.Context, stmt.ReflectValue.Index(i), value) | ||||
| 			} | ||||
| 		} | ||||
| 	case reflect.Struct: | ||||
| 		assignValue = func(field *schema.Field, value interface{}) { | ||||
| 			if stmt.ReflectValue.CanAddr() { | ||||
| 				field.Set(stmt.ReflectValue, value) | ||||
| 				field.Set(stmt.Context, stmt.ReflectValue, value) | ||||
| 			} | ||||
| 		} | ||||
| 	default: | ||||
| @ -165,7 +165,7 @@ func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) { | ||||
| 					exprs := make([]clause.Expression, len(stmt.Schema.PrimaryFields)) | ||||
| 					var notZero bool | ||||
| 					for idx, field := range stmt.Schema.PrimaryFields { | ||||
| 						value, isZero := field.ValueOf(stmt.ReflectValue.Index(i)) | ||||
| 						value, isZero := field.ValueOf(stmt.Context, stmt.ReflectValue.Index(i)) | ||||
| 						exprs[idx] = clause.Eq{Column: field.DBName, Value: value} | ||||
| 						notZero = notZero || !isZero | ||||
| 					} | ||||
| @ -178,7 +178,7 @@ func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) { | ||||
| 			} | ||||
| 		case reflect.Struct: | ||||
| 			for _, field := range stmt.Schema.PrimaryFields { | ||||
| 				if value, isZero := field.ValueOf(stmt.ReflectValue); !isZero { | ||||
| 				if value, isZero := field.ValueOf(stmt.Context, stmt.ReflectValue); !isZero { | ||||
| 					stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Eq{Column: field.DBName, Value: value}}}) | ||||
| 				} | ||||
| 			} | ||||
| @ -258,7 +258,7 @@ func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) { | ||||
| 				if field := updatingSchema.LookUpField(dbName); field != nil { | ||||
| 					if !field.PrimaryKey || !updatingValue.CanAddr() || stmt.Dest != stmt.Model { | ||||
| 						if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && (!restricted || (!stmt.SkipHooks && field.AutoUpdateTime > 0))) { | ||||
| 							value, isZero := field.ValueOf(updatingValue) | ||||
| 							value, isZero := field.ValueOf(stmt.Context, updatingValue) | ||||
| 							if !stmt.SkipHooks && field.AutoUpdateTime > 0 { | ||||
| 								if field.AutoUpdateTime == schema.UnixNanosecond { | ||||
| 									value = stmt.DB.NowFunc().UnixNano() | ||||
| @ -278,7 +278,7 @@ func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) { | ||||
| 							} | ||||
| 						} | ||||
| 					} else { | ||||
| 						if value, isZero := field.ValueOf(updatingValue); !isZero { | ||||
| 						if value, isZero := field.ValueOf(stmt.Context, updatingValue); !isZero { | ||||
| 							stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Eq{Column: field.DBName, Value: value}}}) | ||||
| 						} | ||||
| 					} | ||||
|  | ||||
| @ -83,7 +83,7 @@ func (db *DB) Save(value interface{}) (tx *DB) { | ||||
| 	case reflect.Struct: | ||||
| 		if err := tx.Statement.Parse(value); err == nil && tx.Statement.Schema != nil { | ||||
| 			for _, pf := range tx.Statement.Schema.PrimaryFields { | ||||
| 				if _, isZero := pf.ValueOf(reflectValue); isZero { | ||||
| 				if _, isZero := pf.ValueOf(tx.Statement.Context, reflectValue); isZero { | ||||
| 					return tx.callbacks.Create().Execute(tx) | ||||
| 				} | ||||
| 			} | ||||
| @ -199,7 +199,7 @@ func (db *DB) FindInBatches(dest interface{}, batchSize int, fc func(tx *DB, bat | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		primaryValue, _ := result.Statement.Schema.PrioritizedPrimaryField.ValueOf(resultsValue.Index(resultsValue.Len() - 1)) | ||||
| 		primaryValue, _ := result.Statement.Schema.PrioritizedPrimaryField.ValueOf(tx.Statement.Context, resultsValue.Index(resultsValue.Len()-1)) | ||||
| 		queryDB = tx.Clauses(clause.Gt{Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey}, Value: primaryValue}) | ||||
| 	} | ||||
| 
 | ||||
| @ -216,11 +216,11 @@ func (tx *DB) assignInterfacesToValue(values ...interface{}) { | ||||
| 					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)) | ||||
| 							tx.AddError(field.Set(tx.Statement.Context, 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)) | ||||
| 							tx.AddError(field.Set(tx.Statement.Context, tx.Statement.ReflectValue, eq.Value)) | ||||
| 						} | ||||
| 					} | ||||
| 				} else if andCond, ok := expr.(clause.AndConditions); ok { | ||||
| @ -238,9 +238,9 @@ func (tx *DB) assignInterfacesToValue(values ...interface{}) { | ||||
| 				case reflect.Struct: | ||||
| 					for _, f := range s.Fields { | ||||
| 						if f.Readable { | ||||
| 							if v, isZero := f.ValueOf(reflectValue); !isZero { | ||||
| 							if v, isZero := f.ValueOf(tx.Statement.Context, reflectValue); !isZero { | ||||
| 								if field := tx.Statement.Schema.LookUpField(f.Name); field != nil { | ||||
| 									tx.AddError(field.Set(tx.Statement.ReflectValue, v)) | ||||
| 									tx.AddError(field.Set(tx.Statement.Context, tx.Statement.ReflectValue, v)) | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
|  | ||||
| @ -40,14 +40,17 @@ type SavePointerDialectorInterface interface { | ||||
| 	RollbackTo(tx *DB, name string) error | ||||
| } | ||||
| 
 | ||||
| // TxBeginner tx beginner
 | ||||
| type TxBeginner interface { | ||||
| 	BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) | ||||
| } | ||||
| 
 | ||||
| // ConnPoolBeginner conn pool beginner
 | ||||
| type ConnPoolBeginner interface { | ||||
| 	BeginTx(ctx context.Context, opts *sql.TxOptions) (ConnPool, error) | ||||
| } | ||||
| 
 | ||||
| // TxCommitter tx commiter
 | ||||
| type TxCommitter interface { | ||||
| 	Commit() error | ||||
| 	Rollback() error | ||||
| @ -58,6 +61,7 @@ type Valuer interface { | ||||
| 	GormValue(context.Context, *DB) clause.Expr | ||||
| } | ||||
| 
 | ||||
| // GetDBConnector SQL db connector
 | ||||
| type GetDBConnector interface { | ||||
| 	GetDBConn() (*sql.DB, error) | ||||
| } | ||||
|  | ||||
							
								
								
									
										32
									
								
								scan.go
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								scan.go
									
									
									
									
									
								
							| @ -10,6 +10,7 @@ import ( | ||||
| 	"gorm.io/gorm/schema" | ||||
| ) | ||||
| 
 | ||||
| // prepareValues prepare values slice
 | ||||
| func prepareValues(values []interface{}, db *DB, columnTypes []*sql.ColumnType, columns []string) { | ||||
| 	if db.Statement.Schema != nil { | ||||
| 		for idx, name := range columns { | ||||
| @ -54,11 +55,13 @@ func (db *DB) scanIntoStruct(sch *schema.Schema, rows *sql.Rows, reflectValue re | ||||
| 		if sch == nil { | ||||
| 			values[idx] = reflectValue.Interface() | ||||
| 		} else if field := sch.LookUpField(column); field != nil && field.Readable { | ||||
| 			values[idx] = reflect.New(reflect.PtrTo(field.IndirectFieldType)).Interface() | ||||
| 			values[idx] = field.NewValuePool.Get() | ||||
| 			defer field.NewValuePool.Put(values[idx]) | ||||
| 		} else if names := strings.Split(column, "__"); len(names) > 1 { | ||||
| 			if rel, ok := sch.Relationships.Relations[names[0]]; ok { | ||||
| 				if field := rel.FieldSchema.LookUpField(strings.Join(names[1:], "__")); field != nil && field.Readable { | ||||
| 					values[idx] = reflect.New(reflect.PtrTo(field.IndirectFieldType)).Interface() | ||||
| 					values[idx] = field.NewValuePool.Get() | ||||
| 					defer field.NewValuePool.Put(values[idx]) | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
| @ -77,21 +80,21 @@ func (db *DB) scanIntoStruct(sch *schema.Schema, rows *sql.Rows, reflectValue re | ||||
| 	if sch != nil { | ||||
| 		for idx, column := range columns { | ||||
| 			if field := sch.LookUpField(column); field != nil && field.Readable { | ||||
| 				field.Set(reflectValue, values[idx]) | ||||
| 				field.Set(db.Statement.Context, reflectValue, values[idx]) | ||||
| 			} else if names := strings.Split(column, "__"); len(names) > 1 { | ||||
| 				if rel, ok := sch.Relationships.Relations[names[0]]; ok { | ||||
| 					if field := rel.FieldSchema.LookUpField(strings.Join(names[1:], "__")); field != nil && field.Readable { | ||||
| 						relValue := rel.Field.ReflectValueOf(reflectValue) | ||||
| 						value := reflect.ValueOf(values[idx]).Elem() | ||||
| 						relValue := rel.Field.ReflectValueOf(db.Statement.Context, reflectValue) | ||||
| 
 | ||||
| 						if relValue.Kind() == reflect.Ptr && relValue.IsNil() { | ||||
| 							if value.IsNil() { | ||||
| 							if value := reflect.ValueOf(values[idx]).Elem(); value.Kind() == reflect.Ptr && value.IsNil() { | ||||
| 								continue | ||||
| 							} | ||||
| 
 | ||||
| 							relValue.Set(reflect.New(relValue.Type().Elem())) | ||||
| 						} | ||||
| 
 | ||||
| 						field.Set(relValue, values[idx]) | ||||
| 						field.Set(db.Statement.Context, relValue, values[idx]) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| @ -99,14 +102,17 @@ func (db *DB) scanIntoStruct(sch *schema.Schema, rows *sql.Rows, reflectValue re | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // ScanMode scan data mode
 | ||||
| type ScanMode uint8 | ||||
| 
 | ||||
| // scan modes
 | ||||
| const ( | ||||
| 	ScanInitialized         ScanMode = 1 << 0 // 1
 | ||||
| 	ScanUpdate              ScanMode = 1 << 1 // 2
 | ||||
| 	ScanOnConflictDoNothing ScanMode = 1 << 2 // 4
 | ||||
| ) | ||||
| 
 | ||||
| // Scan scan rows into db statement
 | ||||
| func Scan(rows *sql.Rows, db *DB, mode ScanMode) { | ||||
| 	var ( | ||||
| 		columns, _          = rows.Columns() | ||||
| @ -138,7 +144,7 @@ func Scan(rows *sql.Rows, db *DB, mode ScanMode) { | ||||
| 			} | ||||
| 			scanIntoMap(mapValue, values, columns) | ||||
| 		} | ||||
| 	case *[]map[string]interface{}, []map[string]interface{}: | ||||
| 	case *[]map[string]interface{}: | ||||
| 		columnTypes, _ := rows.ColumnTypes() | ||||
| 		for initialized || rows.Next() { | ||||
| 			prepareValues(values, db, columnTypes, columns) | ||||
| @ -149,11 +155,7 @@ func Scan(rows *sql.Rows, db *DB, mode ScanMode) { | ||||
| 
 | ||||
| 			mapValue := map[string]interface{}{} | ||||
| 			scanIntoMap(mapValue, values, columns) | ||||
| 			if values, ok := dest.([]map[string]interface{}); ok { | ||||
| 				values = append(values, mapValue) | ||||
| 			} else if values, ok := dest.(*[]map[string]interface{}); ok { | ||||
| 				*values = append(*values, mapValue) | ||||
| 			} | ||||
| 			*dest = append(*dest, mapValue) | ||||
| 		} | ||||
| 	case *int, *int8, *int16, *int32, *int64, | ||||
| 		*uint, *uint8, *uint16, *uint32, *uint64, *uintptr, | ||||
| @ -174,7 +176,7 @@ func Scan(rows *sql.Rows, db *DB, mode ScanMode) { | ||||
| 			reflectValue = db.Statement.ReflectValue | ||||
| 		) | ||||
| 
 | ||||
| 		if reflectValue.Kind() == reflect.Interface { | ||||
| 		for reflectValue.Kind() == reflect.Interface { | ||||
| 			reflectValue = reflectValue.Elem() | ||||
| 		} | ||||
| 
 | ||||
| @ -244,7 +246,7 @@ func Scan(rows *sql.Rows, db *DB, mode ScanMode) { | ||||
| 					elem = reflectValue.Index(int(db.RowsAffected)) | ||||
| 					if onConflictDonothing { | ||||
| 						for _, field := range fields { | ||||
| 							if _, ok := field.ValueOf(elem); !ok { | ||||
| 							if _, ok := field.ValueOf(db.Statement.Context, elem); !ok { | ||||
| 								db.RowsAffected++ | ||||
| 								goto BEGIN | ||||
| 							} | ||||
|  | ||||
							
								
								
									
										564
									
								
								schema/field.go
									
									
									
									
									
								
							
							
						
						
									
										564
									
								
								schema/field.go
									
									
									
									
									
								
							| @ -1,6 +1,7 @@ | ||||
| package schema | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"database/sql" | ||||
| 	"database/sql/driver" | ||||
| 	"fmt" | ||||
| @ -14,12 +15,21 @@ import ( | ||||
| 	"gorm.io/gorm/utils" | ||||
| ) | ||||
| 
 | ||||
| type DataType string | ||||
| // special types' reflect type
 | ||||
| var ( | ||||
| 	TimeReflectType    = reflect.TypeOf(time.Time{}) | ||||
| 	TimePtrReflectType = reflect.TypeOf(&time.Time{}) | ||||
| 	ByteReflectType    = reflect.TypeOf(uint8(0)) | ||||
| ) | ||||
| 
 | ||||
| type TimeType int64 | ||||
| 
 | ||||
| var TimeReflectType = reflect.TypeOf(time.Time{}) | ||||
| type ( | ||||
| 	// DataType GORM data type
 | ||||
| 	DataType string | ||||
| 	// TimeType GORM time type
 | ||||
| 	TimeType int64 | ||||
| ) | ||||
| 
 | ||||
| // GORM time types
 | ||||
| const ( | ||||
| 	UnixTime        TimeType = 1 | ||||
| 	UnixSecond      TimeType = 2 | ||||
| @ -27,6 +37,7 @@ const ( | ||||
| 	UnixNanosecond  TimeType = 4 | ||||
| ) | ||||
| 
 | ||||
| // GORM fields types
 | ||||
| const ( | ||||
| 	Bool   DataType = "bool" | ||||
| 	Int    DataType = "int" | ||||
| @ -37,6 +48,7 @@ const ( | ||||
| 	Bytes  DataType = "bytes" | ||||
| ) | ||||
| 
 | ||||
| // Field is the representation of model schema's field
 | ||||
| type Field struct { | ||||
| 	Name                   string | ||||
| 	DBName                 string | ||||
| @ -49,9 +61,9 @@ type Field struct { | ||||
| 	Creatable              bool | ||||
| 	Updatable              bool | ||||
| 	Readable               bool | ||||
| 	HasDefaultValue        bool | ||||
| 	AutoCreateTime         TimeType | ||||
| 	AutoUpdateTime         TimeType | ||||
| 	HasDefaultValue        bool | ||||
| 	DefaultValue           string | ||||
| 	DefaultValueInterface  interface{} | ||||
| 	NotNull                bool | ||||
| @ -60,6 +72,7 @@ type Field struct { | ||||
| 	Size                   int | ||||
| 	Precision              int | ||||
| 	Scale                  int | ||||
| 	IgnoreMigration        bool | ||||
| 	FieldType              reflect.Type | ||||
| 	IndirectFieldType      reflect.Type | ||||
| 	StructField            reflect.StructField | ||||
| @ -68,27 +81,39 @@ type Field struct { | ||||
| 	Schema                 *Schema | ||||
| 	EmbeddedSchema         *Schema | ||||
| 	OwnerSchema            *Schema | ||||
| 	ReflectValueOf         func(reflect.Value) reflect.Value | ||||
| 	ValueOf                func(reflect.Value) (value interface{}, zero bool) | ||||
| 	Set                    func(reflect.Value, interface{}) error | ||||
| 	IgnoreMigration        bool | ||||
| 	ReflectValueOf         func(context.Context, reflect.Value) reflect.Value | ||||
| 	ValueOf                func(context.Context, reflect.Value) (value interface{}, zero bool) | ||||
| 	Set                    func(context.Context, reflect.Value, interface{}) error | ||||
| 	Serializer             SerializerInterface | ||||
| 	NewValuePool           FieldNewValuePool | ||||
| } | ||||
| 
 | ||||
| // ParseField parses reflect.StructField to Field
 | ||||
| func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | ||||
| 	var err error | ||||
| 	var ( | ||||
| 		err        error | ||||
| 		tagSetting = ParseTagSetting(fieldStruct.Tag.Get("gorm"), ";") | ||||
| 	) | ||||
| 
 | ||||
| 	field := &Field{ | ||||
| 		Name:                   fieldStruct.Name, | ||||
| 		DBName:                 tagSetting["COLUMN"], | ||||
| 		BindNames:              []string{fieldStruct.Name}, | ||||
| 		FieldType:              fieldStruct.Type, | ||||
| 		IndirectFieldType:      fieldStruct.Type, | ||||
| 		StructField:            fieldStruct, | ||||
| 		Tag:                    fieldStruct.Tag, | ||||
| 		TagSettings:            tagSetting, | ||||
| 		Schema:                 schema, | ||||
| 		Creatable:              true, | ||||
| 		Updatable:              true, | ||||
| 		Readable:               true, | ||||
| 		Tag:                    fieldStruct.Tag, | ||||
| 		TagSettings:            ParseTagSetting(fieldStruct.Tag.Get("gorm"), ";"), | ||||
| 		Schema:                 schema, | ||||
| 		PrimaryKey:             utils.CheckTruth(tagSetting["PRIMARYKEY"], tagSetting["PRIMARY_KEY"]), | ||||
| 		AutoIncrement:          utils.CheckTruth(tagSetting["AUTOINCREMENT"]), | ||||
| 		HasDefaultValue:        utils.CheckTruth(tagSetting["AUTOINCREMENT"]), | ||||
| 		NotNull:                utils.CheckTruth(tagSetting["NOT NULL"], tagSetting["NOTNULL"]), | ||||
| 		Unique:                 utils.CheckTruth(tagSetting["UNIQUE"]), | ||||
| 		Comment:                tagSetting["COMMENT"], | ||||
| 		AutoIncrementIncrement: 1, | ||||
| 	} | ||||
| 
 | ||||
| @ -97,7 +122,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | ||||
| 	} | ||||
| 
 | ||||
| 	fieldValue := reflect.New(field.IndirectFieldType) | ||||
| 	// if field is valuer, used its value or first fields as data type
 | ||||
| 	// if field is valuer, used its value or first field as data type
 | ||||
| 	valuer, isValuer := fieldValue.Interface().(driver.Valuer) | ||||
| 	if isValuer { | ||||
| 		if _, ok := fieldValue.Interface().(GormDataTypeInterface); !ok { | ||||
| @ -105,31 +130,37 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | ||||
| 				fieldValue = reflect.ValueOf(v) | ||||
| 			} | ||||
| 
 | ||||
| 			// Use the field struct's first field type as data type, e.g: use `string` for sql.NullString
 | ||||
| 			var getRealFieldValue func(reflect.Value) | ||||
| 			getRealFieldValue = func(v reflect.Value) { | ||||
| 				rv := reflect.Indirect(v) | ||||
| 				if rv.Kind() == reflect.Struct && !rv.Type().ConvertibleTo(TimeReflectType) { | ||||
| 					for i := 0; i < rv.Type().NumField(); i++ { | ||||
| 						newFieldType := rv.Type().Field(i).Type | ||||
| 				var ( | ||||
| 					rv     = reflect.Indirect(v) | ||||
| 					rvType = rv.Type() | ||||
| 				) | ||||
| 
 | ||||
| 				if rv.Kind() == reflect.Struct && !rvType.ConvertibleTo(TimeReflectType) { | ||||
| 					for i := 0; i < rvType.NumField(); i++ { | ||||
| 						for key, value := range ParseTagSetting(rvType.Field(i).Tag.Get("gorm"), ";") { | ||||
| 							if _, ok := field.TagSettings[key]; !ok { | ||||
| 								field.TagSettings[key] = value | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					for i := 0; i < rvType.NumField(); i++ { | ||||
| 						newFieldType := rvType.Field(i).Type | ||||
| 						for newFieldType.Kind() == reflect.Ptr { | ||||
| 							newFieldType = newFieldType.Elem() | ||||
| 						} | ||||
| 
 | ||||
| 						fieldValue = reflect.New(newFieldType) | ||||
| 
 | ||||
| 						if rv.Type() != reflect.Indirect(fieldValue).Type() { | ||||
| 						if rvType != reflect.Indirect(fieldValue).Type() { | ||||
| 							getRealFieldValue(fieldValue) | ||||
| 						} | ||||
| 
 | ||||
| 						if fieldValue.IsValid() { | ||||
| 							return | ||||
| 						} | ||||
| 
 | ||||
| 						for key, value := range ParseTagSetting(field.IndirectFieldType.Field(i).Tag.Get("gorm"), ";") { | ||||
| 							if _, ok := field.TagSettings[key]; !ok { | ||||
| 								field.TagSettings[key] = value | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| @ -138,19 +169,23 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if dbName, ok := field.TagSettings["COLUMN"]; ok { | ||||
| 		field.DBName = dbName | ||||
| 	} | ||||
| 
 | ||||
| 	if val, ok := field.TagSettings["PRIMARYKEY"]; ok && utils.CheckTruth(val) { | ||||
| 		field.PrimaryKey = true | ||||
| 	} else if val, ok := field.TagSettings["PRIMARY_KEY"]; ok && utils.CheckTruth(val) { | ||||
| 		field.PrimaryKey = true | ||||
| 	} | ||||
| 
 | ||||
| 	if val, ok := field.TagSettings["AUTOINCREMENT"]; ok && utils.CheckTruth(val) { | ||||
| 		field.AutoIncrement = true | ||||
| 		field.HasDefaultValue = true | ||||
| 	if v, isSerializer := fieldValue.Interface().(SerializerInterface); isSerializer { | ||||
| 		field.DataType = String | ||||
| 		field.Serializer = v | ||||
| 	} else { | ||||
| 		var serializerName = field.TagSettings["JSON"] | ||||
| 		if serializerName == "" { | ||||
| 			serializerName = field.TagSettings["SERIALIZER"] | ||||
| 		} | ||||
| 		if serializerName != "" { | ||||
| 			if serializer, ok := GetSerializer(serializerName); ok { | ||||
| 				// Set default data type to string for serializer
 | ||||
| 				field.DataType = String | ||||
| 				field.Serializer = serializer | ||||
| 			} else { | ||||
| 				schema.err = fmt.Errorf("invalid serializer type %v", serializerName) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if num, ok := field.TagSettings["AUTOINCREMENTINCREMENT"]; ok { | ||||
| @ -176,20 +211,6 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | ||||
| 		field.Scale, _ = strconv.Atoi(s) | ||||
| 	} | ||||
| 
 | ||||
| 	if val, ok := field.TagSettings["NOT NULL"]; ok && utils.CheckTruth(val) { | ||||
| 		field.NotNull = true | ||||
| 	} else if val, ok := field.TagSettings["NOTNULL"]; ok && utils.CheckTruth(val) { | ||||
| 		field.NotNull = true | ||||
| 	} | ||||
| 
 | ||||
| 	if val, ok := field.TagSettings["UNIQUE"]; ok && utils.CheckTruth(val) { | ||||
| 		field.Unique = true | ||||
| 	} | ||||
| 
 | ||||
| 	if val, ok := field.TagSettings["COMMENT"]; ok { | ||||
| 		field.Comment = val | ||||
| 	} | ||||
| 
 | ||||
| 	// default value is function or null or blank (primary keys)
 | ||||
| 	field.DefaultValue = strings.TrimSpace(field.DefaultValue) | ||||
| 	skipParseDefaultValue := strings.Contains(field.DefaultValue, "(") && | ||||
| @ -225,7 +246,6 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | ||||
| 		} | ||||
| 	case reflect.String: | ||||
| 		field.DataType = String | ||||
| 
 | ||||
| 		if field.HasDefaultValue && !skipParseDefaultValue { | ||||
| 			field.DefaultValue = strings.Trim(field.DefaultValue, "'") | ||||
| 			field.DefaultValue = strings.Trim(field.DefaultValue, `"`) | ||||
| @ -236,17 +256,15 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | ||||
| 			field.DataType = Time | ||||
| 		} else if fieldValue.Type().ConvertibleTo(TimeReflectType) { | ||||
| 			field.DataType = Time | ||||
| 		} else if fieldValue.Type().ConvertibleTo(reflect.TypeOf(&time.Time{})) { | ||||
| 		} else if fieldValue.Type().ConvertibleTo(TimePtrReflectType) { | ||||
| 			field.DataType = Time | ||||
| 		} | ||||
| 	case reflect.Array, reflect.Slice: | ||||
| 		if reflect.Indirect(fieldValue).Type().Elem() == reflect.TypeOf(uint8(0)) { | ||||
| 		if reflect.Indirect(fieldValue).Type().Elem() == ByteReflectType && field.DataType == "" { | ||||
| 			field.DataType = Bytes | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	field.GORMDataType = field.DataType | ||||
| 
 | ||||
| 	if dataTyper, ok := fieldValue.Interface().(GormDataTypeInterface); ok { | ||||
| 		field.DataType = DataType(dataTyper.GormDataType()) | ||||
| 	} | ||||
| @ -346,8 +364,9 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if _, ok := field.TagSettings["EMBEDDED"]; field.GORMDataType != Time && field.GORMDataType != Bytes && | ||||
| 		(ok || (fieldStruct.Anonymous && !isValuer && (field.Creatable || field.Updatable || field.Readable))) { | ||||
| 	// Normal anonymous field or having `EMBEDDED` tag
 | ||||
| 	if _, ok := field.TagSettings["EMBEDDED"]; ok || (field.GORMDataType != Time && field.GORMDataType != Bytes && !isValuer && | ||||
| 		fieldStruct.Anonymous && (field.Creatable || field.Updatable || field.Readable)) { | ||||
| 		kind := reflect.Indirect(fieldValue).Kind() | ||||
| 		switch kind { | ||||
| 		case reflect.Struct: | ||||
| @ -410,95 +429,122 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | ||||
| 
 | ||||
| // create valuer, setter when parse struct
 | ||||
| func (field *Field) setupValuerAndSetter() { | ||||
| 	// ValueOf
 | ||||
| 	switch { | ||||
| 	case len(field.StructField.Index) == 1: | ||||
| 		field.ValueOf = func(value reflect.Value) (interface{}, bool) { | ||||
| 			fieldValue := reflect.Indirect(value).Field(field.StructField.Index[0]) | ||||
| 			return fieldValue.Interface(), fieldValue.IsZero() | ||||
| 		} | ||||
| 	case len(field.StructField.Index) == 2 && field.StructField.Index[0] >= 0: | ||||
| 		field.ValueOf = func(value reflect.Value) (interface{}, bool) { | ||||
| 			fieldValue := reflect.Indirect(value).Field(field.StructField.Index[0]).Field(field.StructField.Index[1]) | ||||
| 			return fieldValue.Interface(), fieldValue.IsZero() | ||||
| 		} | ||||
| 	default: | ||||
| 		field.ValueOf = func(value reflect.Value) (interface{}, bool) { | ||||
| 			v := reflect.Indirect(value) | ||||
| 
 | ||||
| 			for _, idx := range field.StructField.Index { | ||||
| 				if idx >= 0 { | ||||
| 					v = v.Field(idx) | ||||
| 				} else { | ||||
| 					v = v.Field(-idx - 1) | ||||
| 
 | ||||
| 					if v.Type().Elem().Kind() != reflect.Struct { | ||||
| 						return nil, true | ||||
| 					} | ||||
| 
 | ||||
| 					if !v.IsNil() { | ||||
| 						v = v.Elem() | ||||
| 					} else { | ||||
| 						return nil, true | ||||
| 					} | ||||
| 	// Setup NewValuePool
 | ||||
| 	var fieldValue = reflect.New(field.FieldType).Interface() | ||||
| 	if field.Serializer != nil { | ||||
| 		field.NewValuePool = &sync.Pool{ | ||||
| 			New: func() interface{} { | ||||
| 				return &serializer{ | ||||
| 					Field:      field, | ||||
| 					Serializer: reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface), | ||||
| 				} | ||||
| 			}, | ||||
| 		} | ||||
| 	} else if _, ok := fieldValue.(sql.Scanner); !ok { | ||||
| 		// set default NewValuePool
 | ||||
| 		switch field.IndirectFieldType.Kind() { | ||||
| 		case reflect.String: | ||||
| 			field.NewValuePool = stringPool | ||||
| 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||
| 			field.NewValuePool = intPool | ||||
| 		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | ||||
| 			field.NewValuePool = uintPool | ||||
| 		case reflect.Float32, reflect.Float64: | ||||
| 			field.NewValuePool = floatPool | ||||
| 		case reflect.Bool: | ||||
| 			field.NewValuePool = boolPool | ||||
| 		default: | ||||
| 			if field.IndirectFieldType == TimeReflectType { | ||||
| 				field.NewValuePool = timePool | ||||
| 			} | ||||
| 			return v.Interface(), v.IsZero() | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// ReflectValueOf
 | ||||
| 	switch { | ||||
| 	case len(field.StructField.Index) == 1: | ||||
| 		field.ReflectValueOf = func(value reflect.Value) reflect.Value { | ||||
| 			return reflect.Indirect(value).Field(field.StructField.Index[0]) | ||||
| 		} | ||||
| 	case len(field.StructField.Index) == 2 && field.StructField.Index[0] >= 0 && field.FieldType.Kind() != reflect.Ptr: | ||||
| 		field.ReflectValueOf = func(value reflect.Value) reflect.Value { | ||||
| 			return reflect.Indirect(value).Field(field.StructField.Index[0]).Field(field.StructField.Index[1]) | ||||
| 		} | ||||
| 	default: | ||||
| 		field.ReflectValueOf = func(value reflect.Value) reflect.Value { | ||||
| 			v := reflect.Indirect(value) | ||||
| 			for idx, fieldIdx := range field.StructField.Index { | ||||
| 				if fieldIdx >= 0 { | ||||
| 					v = v.Field(fieldIdx) | ||||
| 	if field.NewValuePool == nil { | ||||
| 		field.NewValuePool = poolInitializer(reflect.PtrTo(field.IndirectFieldType)) | ||||
| 	} | ||||
| 
 | ||||
| 	// ValueOf returns field's value and if it is zero
 | ||||
| 	field.ValueOf = func(ctx context.Context, v reflect.Value) (interface{}, bool) { | ||||
| 		v = reflect.Indirect(v) | ||||
| 		for _, fieldIdx := range field.StructField.Index { | ||||
| 			if fieldIdx >= 0 { | ||||
| 				v = v.Field(fieldIdx) | ||||
| 			} else { | ||||
| 				v = v.Field(-fieldIdx - 1) | ||||
| 
 | ||||
| 				if !v.IsNil() { | ||||
| 					v = v.Elem() | ||||
| 				} else { | ||||
| 					v = v.Field(-fieldIdx - 1) | ||||
| 				} | ||||
| 
 | ||||
| 				if v.Kind() == reflect.Ptr { | ||||
| 					if v.Type().Elem().Kind() == reflect.Struct { | ||||
| 						if v.IsNil() { | ||||
| 							v.Set(reflect.New(v.Type().Elem())) | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					if idx < len(field.StructField.Index)-1 { | ||||
| 						v = v.Elem() | ||||
| 					} | ||||
| 					return nil, true | ||||
| 				} | ||||
| 			} | ||||
| 			return v | ||||
| 		} | ||||
| 
 | ||||
| 		fv, zero := v.Interface(), v.IsZero() | ||||
| 		return fv, zero | ||||
| 	} | ||||
| 
 | ||||
| 	if field.Serializer != nil { | ||||
| 		oldValuerOf := field.ValueOf | ||||
| 		field.ValueOf = func(ctx context.Context, v reflect.Value) (interface{}, bool) { | ||||
| 			value, zero := oldValuerOf(ctx, v) | ||||
| 			if zero { | ||||
| 				return value, zero | ||||
| 			} | ||||
| 
 | ||||
| 			s, ok := value.(SerializerValuerInterface) | ||||
| 			if !ok { | ||||
| 				s = field.Serializer | ||||
| 			} | ||||
| 
 | ||||
| 			return serializer{ | ||||
| 				Field:           field, | ||||
| 				SerializeValuer: s, | ||||
| 				Destination:     v, | ||||
| 				Context:         ctx, | ||||
| 				fieldValue:      value, | ||||
| 			}, false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fallbackSetter := func(value reflect.Value, v interface{}, setter func(reflect.Value, interface{}) error) (err error) { | ||||
| 	// ReflectValueOf returns field's reflect value
 | ||||
| 	field.ReflectValueOf = func(ctx context.Context, v reflect.Value) reflect.Value { | ||||
| 		v = reflect.Indirect(v) | ||||
| 		for idx, fieldIdx := range field.StructField.Index { | ||||
| 			if fieldIdx >= 0 { | ||||
| 				v = v.Field(fieldIdx) | ||||
| 			} else { | ||||
| 				v = v.Field(-fieldIdx - 1) | ||||
| 
 | ||||
| 				if v.IsNil() { | ||||
| 					v.Set(reflect.New(v.Type().Elem())) | ||||
| 				} | ||||
| 
 | ||||
| 				if idx < len(field.StructField.Index)-1 { | ||||
| 					v = v.Elem() | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return v | ||||
| 	} | ||||
| 
 | ||||
| 	fallbackSetter := func(ctx context.Context, value reflect.Value, v interface{}, setter func(context.Context, reflect.Value, interface{}) error) (err error) { | ||||
| 		if v == nil { | ||||
| 			field.ReflectValueOf(value).Set(reflect.New(field.FieldType).Elem()) | ||||
| 			field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem()) | ||||
| 		} else { | ||||
| 			reflectV := reflect.ValueOf(v) | ||||
| 			// Optimal value type acquisition for v
 | ||||
| 			reflectValType := reflectV.Type() | ||||
| 
 | ||||
| 			if reflectValType.AssignableTo(field.FieldType) { | ||||
| 				field.ReflectValueOf(value).Set(reflectV) | ||||
| 				field.ReflectValueOf(ctx, value).Set(reflectV) | ||||
| 				return | ||||
| 			} else if reflectValType.ConvertibleTo(field.FieldType) { | ||||
| 				field.ReflectValueOf(value).Set(reflectV.Convert(field.FieldType)) | ||||
| 				field.ReflectValueOf(ctx, value).Set(reflectV.Convert(field.FieldType)) | ||||
| 				return | ||||
| 			} else if field.FieldType.Kind() == reflect.Ptr { | ||||
| 				fieldValue := field.ReflectValueOf(value) | ||||
| 				fieldValue := field.ReflectValueOf(ctx, value) | ||||
| 				fieldType := field.FieldType.Elem() | ||||
| 
 | ||||
| 				if reflectValType.AssignableTo(fieldType) { | ||||
| @ -521,13 +567,16 @@ func (field *Field) setupValuerAndSetter() { | ||||
| 
 | ||||
| 			if reflectV.Kind() == reflect.Ptr { | ||||
| 				if reflectV.IsNil() { | ||||
| 					field.ReflectValueOf(value).Set(reflect.New(field.FieldType).Elem()) | ||||
| 					field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem()) | ||||
| 				} else if reflectV.Type().Elem().AssignableTo(field.FieldType) { | ||||
| 					field.ReflectValueOf(ctx, value).Set(reflectV.Elem()) | ||||
| 					return | ||||
| 				} else { | ||||
| 					err = setter(value, reflectV.Elem().Interface()) | ||||
| 					err = setter(ctx, value, reflectV.Elem().Interface()) | ||||
| 				} | ||||
| 			} else if valuer, ok := v.(driver.Valuer); ok { | ||||
| 				if v, err = valuer.Value(); err == nil { | ||||
| 					err = setter(value, v) | ||||
| 					err = setter(ctx, value, v) | ||||
| 				} | ||||
| 			} else { | ||||
| 				return fmt.Errorf("failed to set value %+v to field %s", v, field.Name) | ||||
| @ -540,191 +589,201 @@ func (field *Field) setupValuerAndSetter() { | ||||
| 	// Set
 | ||||
| 	switch field.FieldType.Kind() { | ||||
| 	case reflect.Bool: | ||||
| 		field.Set = func(value reflect.Value, v interface{}) error { | ||||
| 		field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error { | ||||
| 			switch data := v.(type) { | ||||
| 			case **bool: | ||||
| 				if data != nil && *data != nil { | ||||
| 					field.ReflectValueOf(ctx, value).SetBool(**data) | ||||
| 				} | ||||
| 			case bool: | ||||
| 				field.ReflectValueOf(value).SetBool(data) | ||||
| 			case *bool: | ||||
| 				if data != nil { | ||||
| 					field.ReflectValueOf(value).SetBool(*data) | ||||
| 				} else { | ||||
| 					field.ReflectValueOf(value).SetBool(false) | ||||
| 				} | ||||
| 				field.ReflectValueOf(ctx, value).SetBool(data) | ||||
| 			case int64: | ||||
| 				if data > 0 { | ||||
| 					field.ReflectValueOf(value).SetBool(true) | ||||
| 				} else { | ||||
| 					field.ReflectValueOf(value).SetBool(false) | ||||
| 				} | ||||
| 				field.ReflectValueOf(ctx, value).SetBool(data > 0) | ||||
| 			case string: | ||||
| 				b, _ := strconv.ParseBool(data) | ||||
| 				field.ReflectValueOf(value).SetBool(b) | ||||
| 				field.ReflectValueOf(ctx, value).SetBool(b) | ||||
| 			default: | ||||
| 				return fallbackSetter(value, v, field.Set) | ||||
| 				return fallbackSetter(ctx, value, v, field.Set) | ||||
| 			} | ||||
| 			return nil | ||||
| 		} | ||||
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||
| 		field.Set = func(value reflect.Value, v interface{}) (err error) { | ||||
| 		field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { | ||||
| 			switch data := v.(type) { | ||||
| 			case **int64: | ||||
| 				if data != nil && *data != nil { | ||||
| 					field.ReflectValueOf(ctx, value).SetInt(**data) | ||||
| 				} | ||||
| 			case int64: | ||||
| 				field.ReflectValueOf(value).SetInt(data) | ||||
| 				field.ReflectValueOf(ctx, value).SetInt(data) | ||||
| 			case int: | ||||
| 				field.ReflectValueOf(value).SetInt(int64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetInt(int64(data)) | ||||
| 			case int8: | ||||
| 				field.ReflectValueOf(value).SetInt(int64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetInt(int64(data)) | ||||
| 			case int16: | ||||
| 				field.ReflectValueOf(value).SetInt(int64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetInt(int64(data)) | ||||
| 			case int32: | ||||
| 				field.ReflectValueOf(value).SetInt(int64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetInt(int64(data)) | ||||
| 			case uint: | ||||
| 				field.ReflectValueOf(value).SetInt(int64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetInt(int64(data)) | ||||
| 			case uint8: | ||||
| 				field.ReflectValueOf(value).SetInt(int64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetInt(int64(data)) | ||||
| 			case uint16: | ||||
| 				field.ReflectValueOf(value).SetInt(int64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetInt(int64(data)) | ||||
| 			case uint32: | ||||
| 				field.ReflectValueOf(value).SetInt(int64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetInt(int64(data)) | ||||
| 			case uint64: | ||||
| 				field.ReflectValueOf(value).SetInt(int64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetInt(int64(data)) | ||||
| 			case float32: | ||||
| 				field.ReflectValueOf(value).SetInt(int64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetInt(int64(data)) | ||||
| 			case float64: | ||||
| 				field.ReflectValueOf(value).SetInt(int64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetInt(int64(data)) | ||||
| 			case []byte: | ||||
| 				return field.Set(value, string(data)) | ||||
| 				return field.Set(ctx, value, string(data)) | ||||
| 			case string: | ||||
| 				if i, err := strconv.ParseInt(data, 0, 64); err == nil { | ||||
| 					field.ReflectValueOf(value).SetInt(i) | ||||
| 					field.ReflectValueOf(ctx, value).SetInt(i) | ||||
| 				} else { | ||||
| 					return err | ||||
| 				} | ||||
| 			case time.Time: | ||||
| 				if field.AutoCreateTime == UnixNanosecond || field.AutoUpdateTime == UnixNanosecond { | ||||
| 					field.ReflectValueOf(value).SetInt(data.UnixNano()) | ||||
| 					field.ReflectValueOf(ctx, value).SetInt(data.UnixNano()) | ||||
| 				} else if field.AutoCreateTime == UnixMillisecond || field.AutoUpdateTime == UnixMillisecond { | ||||
| 					field.ReflectValueOf(value).SetInt(data.UnixNano() / 1e6) | ||||
| 					field.ReflectValueOf(ctx, value).SetInt(data.UnixNano() / 1e6) | ||||
| 				} else { | ||||
| 					field.ReflectValueOf(value).SetInt(data.Unix()) | ||||
| 					field.ReflectValueOf(ctx, value).SetInt(data.Unix()) | ||||
| 				} | ||||
| 			case *time.Time: | ||||
| 				if data != nil { | ||||
| 					if field.AutoCreateTime == UnixNanosecond || field.AutoUpdateTime == UnixNanosecond { | ||||
| 						field.ReflectValueOf(value).SetInt(data.UnixNano()) | ||||
| 						field.ReflectValueOf(ctx, value).SetInt(data.UnixNano()) | ||||
| 					} else if field.AutoCreateTime == UnixMillisecond || field.AutoUpdateTime == UnixMillisecond { | ||||
| 						field.ReflectValueOf(value).SetInt(data.UnixNano() / 1e6) | ||||
| 						field.ReflectValueOf(ctx, value).SetInt(data.UnixNano() / 1e6) | ||||
| 					} else { | ||||
| 						field.ReflectValueOf(value).SetInt(data.Unix()) | ||||
| 						field.ReflectValueOf(ctx, value).SetInt(data.Unix()) | ||||
| 					} | ||||
| 				} else { | ||||
| 					field.ReflectValueOf(value).SetInt(0) | ||||
| 					field.ReflectValueOf(ctx, value).SetInt(0) | ||||
| 				} | ||||
| 			default: | ||||
| 				return fallbackSetter(value, v, field.Set) | ||||
| 				return fallbackSetter(ctx, value, v, field.Set) | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
| 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | ||||
| 		field.Set = func(value reflect.Value, v interface{}) (err error) { | ||||
| 		field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { | ||||
| 			switch data := v.(type) { | ||||
| 			case **uint64: | ||||
| 				if data != nil && *data != nil { | ||||
| 					field.ReflectValueOf(ctx, value).SetUint(**data) | ||||
| 				} | ||||
| 			case uint64: | ||||
| 				field.ReflectValueOf(value).SetUint(data) | ||||
| 				field.ReflectValueOf(ctx, value).SetUint(data) | ||||
| 			case uint: | ||||
| 				field.ReflectValueOf(value).SetUint(uint64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetUint(uint64(data)) | ||||
| 			case uint8: | ||||
| 				field.ReflectValueOf(value).SetUint(uint64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetUint(uint64(data)) | ||||
| 			case uint16: | ||||
| 				field.ReflectValueOf(value).SetUint(uint64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetUint(uint64(data)) | ||||
| 			case uint32: | ||||
| 				field.ReflectValueOf(value).SetUint(uint64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetUint(uint64(data)) | ||||
| 			case int64: | ||||
| 				field.ReflectValueOf(value).SetUint(uint64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetUint(uint64(data)) | ||||
| 			case int: | ||||
| 				field.ReflectValueOf(value).SetUint(uint64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetUint(uint64(data)) | ||||
| 			case int8: | ||||
| 				field.ReflectValueOf(value).SetUint(uint64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetUint(uint64(data)) | ||||
| 			case int16: | ||||
| 				field.ReflectValueOf(value).SetUint(uint64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetUint(uint64(data)) | ||||
| 			case int32: | ||||
| 				field.ReflectValueOf(value).SetUint(uint64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetUint(uint64(data)) | ||||
| 			case float32: | ||||
| 				field.ReflectValueOf(value).SetUint(uint64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetUint(uint64(data)) | ||||
| 			case float64: | ||||
| 				field.ReflectValueOf(value).SetUint(uint64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetUint(uint64(data)) | ||||
| 			case []byte: | ||||
| 				return field.Set(value, string(data)) | ||||
| 				return field.Set(ctx, value, string(data)) | ||||
| 			case time.Time: | ||||
| 				if field.AutoCreateTime == UnixNanosecond || field.AutoUpdateTime == UnixNanosecond { | ||||
| 					field.ReflectValueOf(value).SetUint(uint64(data.UnixNano())) | ||||
| 					field.ReflectValueOf(ctx, value).SetUint(uint64(data.UnixNano())) | ||||
| 				} else if field.AutoCreateTime == UnixMillisecond || field.AutoUpdateTime == UnixMillisecond { | ||||
| 					field.ReflectValueOf(value).SetUint(uint64(data.UnixNano() / 1e6)) | ||||
| 					field.ReflectValueOf(ctx, value).SetUint(uint64(data.UnixNano() / 1e6)) | ||||
| 				} else { | ||||
| 					field.ReflectValueOf(value).SetUint(uint64(data.Unix())) | ||||
| 					field.ReflectValueOf(ctx, value).SetUint(uint64(data.Unix())) | ||||
| 				} | ||||
| 			case string: | ||||
| 				if i, err := strconv.ParseUint(data, 0, 64); err == nil { | ||||
| 					field.ReflectValueOf(value).SetUint(i) | ||||
| 					field.ReflectValueOf(ctx, value).SetUint(i) | ||||
| 				} else { | ||||
| 					return err | ||||
| 				} | ||||
| 			default: | ||||
| 				return fallbackSetter(value, v, field.Set) | ||||
| 				return fallbackSetter(ctx, value, v, field.Set) | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
| 	case reflect.Float32, reflect.Float64: | ||||
| 		field.Set = func(value reflect.Value, v interface{}) (err error) { | ||||
| 		field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { | ||||
| 			switch data := v.(type) { | ||||
| 			case **float64: | ||||
| 				if data != nil && *data != nil { | ||||
| 					field.ReflectValueOf(ctx, value).SetFloat(**data) | ||||
| 				} | ||||
| 			case float64: | ||||
| 				field.ReflectValueOf(value).SetFloat(data) | ||||
| 				field.ReflectValueOf(ctx, value).SetFloat(data) | ||||
| 			case float32: | ||||
| 				field.ReflectValueOf(value).SetFloat(float64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetFloat(float64(data)) | ||||
| 			case int64: | ||||
| 				field.ReflectValueOf(value).SetFloat(float64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetFloat(float64(data)) | ||||
| 			case int: | ||||
| 				field.ReflectValueOf(value).SetFloat(float64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetFloat(float64(data)) | ||||
| 			case int8: | ||||
| 				field.ReflectValueOf(value).SetFloat(float64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetFloat(float64(data)) | ||||
| 			case int16: | ||||
| 				field.ReflectValueOf(value).SetFloat(float64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetFloat(float64(data)) | ||||
| 			case int32: | ||||
| 				field.ReflectValueOf(value).SetFloat(float64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetFloat(float64(data)) | ||||
| 			case uint: | ||||
| 				field.ReflectValueOf(value).SetFloat(float64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetFloat(float64(data)) | ||||
| 			case uint8: | ||||
| 				field.ReflectValueOf(value).SetFloat(float64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetFloat(float64(data)) | ||||
| 			case uint16: | ||||
| 				field.ReflectValueOf(value).SetFloat(float64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetFloat(float64(data)) | ||||
| 			case uint32: | ||||
| 				field.ReflectValueOf(value).SetFloat(float64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetFloat(float64(data)) | ||||
| 			case uint64: | ||||
| 				field.ReflectValueOf(value).SetFloat(float64(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetFloat(float64(data)) | ||||
| 			case []byte: | ||||
| 				return field.Set(value, string(data)) | ||||
| 				return field.Set(ctx, value, string(data)) | ||||
| 			case string: | ||||
| 				if i, err := strconv.ParseFloat(data, 64); err == nil { | ||||
| 					field.ReflectValueOf(value).SetFloat(i) | ||||
| 					field.ReflectValueOf(ctx, value).SetFloat(i) | ||||
| 				} else { | ||||
| 					return err | ||||
| 				} | ||||
| 			default: | ||||
| 				return fallbackSetter(value, v, field.Set) | ||||
| 				return fallbackSetter(ctx, value, v, field.Set) | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
| 	case reflect.String: | ||||
| 		field.Set = func(value reflect.Value, v interface{}) (err error) { | ||||
| 		field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { | ||||
| 			switch data := v.(type) { | ||||
| 			case **string: | ||||
| 				if data != nil && *data != nil { | ||||
| 					field.ReflectValueOf(ctx, value).SetString(**data) | ||||
| 				} | ||||
| 			case string: | ||||
| 				field.ReflectValueOf(value).SetString(data) | ||||
| 				field.ReflectValueOf(ctx, value).SetString(data) | ||||
| 			case []byte: | ||||
| 				field.ReflectValueOf(value).SetString(string(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetString(string(data)) | ||||
| 			case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: | ||||
| 				field.ReflectValueOf(value).SetString(utils.ToString(data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetString(utils.ToString(data)) | ||||
| 			case float64, float32: | ||||
| 				field.ReflectValueOf(value).SetString(fmt.Sprintf("%."+strconv.Itoa(field.Precision)+"f", data)) | ||||
| 				field.ReflectValueOf(ctx, value).SetString(fmt.Sprintf("%."+strconv.Itoa(field.Precision)+"f", data)) | ||||
| 			default: | ||||
| 				return fallbackSetter(value, v, field.Set) | ||||
| 				return fallbackSetter(ctx, value, v, field.Set) | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
| @ -732,41 +791,49 @@ func (field *Field) setupValuerAndSetter() { | ||||
| 		fieldValue := reflect.New(field.FieldType) | ||||
| 		switch fieldValue.Elem().Interface().(type) { | ||||
| 		case time.Time: | ||||
| 			field.Set = func(value reflect.Value, v interface{}) error { | ||||
| 			field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error { | ||||
| 				switch data := v.(type) { | ||||
| 				case **time.Time: | ||||
| 					if data != nil && *data != nil { | ||||
| 						field.Set(ctx, value, *data) | ||||
| 					} | ||||
| 				case time.Time: | ||||
| 					field.ReflectValueOf(value).Set(reflect.ValueOf(v)) | ||||
| 					field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(v)) | ||||
| 				case *time.Time: | ||||
| 					if data != nil { | ||||
| 						field.ReflectValueOf(value).Set(reflect.ValueOf(data).Elem()) | ||||
| 						field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(data).Elem()) | ||||
| 					} else { | ||||
| 						field.ReflectValueOf(value).Set(reflect.ValueOf(time.Time{})) | ||||
| 						field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(time.Time{})) | ||||
| 					} | ||||
| 				case string: | ||||
| 					if t, err := now.Parse(data); err == nil { | ||||
| 						field.ReflectValueOf(value).Set(reflect.ValueOf(t)) | ||||
| 						field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(t)) | ||||
| 					} else { | ||||
| 						return fmt.Errorf("failed to set string %v to time.Time field %s, failed to parse it as time, got error %v", v, field.Name, err) | ||||
| 					} | ||||
| 				default: | ||||
| 					return fallbackSetter(value, v, field.Set) | ||||
| 					return fallbackSetter(ctx, value, v, field.Set) | ||||
| 				} | ||||
| 				return nil | ||||
| 			} | ||||
| 		case *time.Time: | ||||
| 			field.Set = func(value reflect.Value, v interface{}) error { | ||||
| 			field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error { | ||||
| 				switch data := v.(type) { | ||||
| 				case **time.Time: | ||||
| 					if data != nil { | ||||
| 						field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(*data)) | ||||
| 					} | ||||
| 				case time.Time: | ||||
| 					fieldValue := field.ReflectValueOf(value) | ||||
| 					fieldValue := field.ReflectValueOf(ctx, value) | ||||
| 					if fieldValue.IsNil() { | ||||
| 						fieldValue.Set(reflect.New(field.FieldType.Elem())) | ||||
| 					} | ||||
| 					fieldValue.Elem().Set(reflect.ValueOf(v)) | ||||
| 				case *time.Time: | ||||
| 					field.ReflectValueOf(value).Set(reflect.ValueOf(v)) | ||||
| 					field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(v)) | ||||
| 				case string: | ||||
| 					if t, err := now.Parse(data); err == nil { | ||||
| 						fieldValue := field.ReflectValueOf(value) | ||||
| 						fieldValue := field.ReflectValueOf(ctx, value) | ||||
| 						if fieldValue.IsNil() { | ||||
| 							if v == "" { | ||||
| 								return nil | ||||
| @ -778,27 +845,27 @@ func (field *Field) setupValuerAndSetter() { | ||||
| 						return fmt.Errorf("failed to set string %v to time.Time field %s, failed to parse it as time, got error %v", v, field.Name, err) | ||||
| 					} | ||||
| 				default: | ||||
| 					return fallbackSetter(value, v, field.Set) | ||||
| 					return fallbackSetter(ctx, value, v, field.Set) | ||||
| 				} | ||||
| 				return nil | ||||
| 			} | ||||
| 		default: | ||||
| 			if _, ok := fieldValue.Elem().Interface().(sql.Scanner); ok { | ||||
| 				// pointer scanner
 | ||||
| 				field.Set = func(value reflect.Value, v interface{}) (err error) { | ||||
| 				field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { | ||||
| 					reflectV := reflect.ValueOf(v) | ||||
| 					if !reflectV.IsValid() { | ||||
| 						field.ReflectValueOf(value).Set(reflect.New(field.FieldType).Elem()) | ||||
| 						field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem()) | ||||
| 					} else if reflectV.Type().AssignableTo(field.FieldType) { | ||||
| 						field.ReflectValueOf(value).Set(reflectV) | ||||
| 						field.ReflectValueOf(ctx, value).Set(reflectV) | ||||
| 					} else if reflectV.Kind() == reflect.Ptr { | ||||
| 						if reflectV.IsNil() || !reflectV.IsValid() { | ||||
| 							field.ReflectValueOf(value).Set(reflect.New(field.FieldType).Elem()) | ||||
| 							field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem()) | ||||
| 						} else { | ||||
| 							return field.Set(value, reflectV.Elem().Interface()) | ||||
| 							return field.Set(ctx, value, reflectV.Elem().Interface()) | ||||
| 						} | ||||
| 					} else { | ||||
| 						fieldValue := field.ReflectValueOf(value) | ||||
| 						fieldValue := field.ReflectValueOf(ctx, value) | ||||
| 						if fieldValue.IsNil() { | ||||
| 							fieldValue.Set(reflect.New(field.FieldType.Elem())) | ||||
| 						} | ||||
| @ -813,32 +880,61 @@ func (field *Field) setupValuerAndSetter() { | ||||
| 				} | ||||
| 			} else if _, ok := fieldValue.Interface().(sql.Scanner); ok { | ||||
| 				// struct scanner
 | ||||
| 				field.Set = func(value reflect.Value, v interface{}) (err error) { | ||||
| 				field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { | ||||
| 					reflectV := reflect.ValueOf(v) | ||||
| 					if !reflectV.IsValid() { | ||||
| 						field.ReflectValueOf(value).Set(reflect.New(field.FieldType).Elem()) | ||||
| 						field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem()) | ||||
| 					} else if reflectV.Type().AssignableTo(field.FieldType) { | ||||
| 						field.ReflectValueOf(value).Set(reflectV) | ||||
| 						field.ReflectValueOf(ctx, value).Set(reflectV) | ||||
| 					} else if reflectV.Kind() == reflect.Ptr { | ||||
| 						if reflectV.IsNil() || !reflectV.IsValid() { | ||||
| 							field.ReflectValueOf(value).Set(reflect.New(field.FieldType).Elem()) | ||||
| 							field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem()) | ||||
| 						} else { | ||||
| 							return field.Set(value, reflectV.Elem().Interface()) | ||||
| 							return field.Set(ctx, value, reflectV.Elem().Interface()) | ||||
| 						} | ||||
| 					} else { | ||||
| 						if valuer, ok := v.(driver.Valuer); ok { | ||||
| 							v, _ = valuer.Value() | ||||
| 						} | ||||
| 
 | ||||
| 						err = field.ReflectValueOf(value).Addr().Interface().(sql.Scanner).Scan(v) | ||||
| 						err = field.ReflectValueOf(ctx, value).Addr().Interface().(sql.Scanner).Scan(v) | ||||
| 					} | ||||
| 					return | ||||
| 				} | ||||
| 			} else { | ||||
| 				field.Set = func(value reflect.Value, v interface{}) (err error) { | ||||
| 					return fallbackSetter(value, v, field.Set) | ||||
| 				field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { | ||||
| 					return fallbackSetter(ctx, value, v, field.Set) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if field.Serializer != nil { | ||||
| 		var ( | ||||
| 			oldFieldSetter = field.Set | ||||
| 			sameElemType   bool | ||||
| 			sameType       = field.FieldType == reflect.ValueOf(field.Serializer).Type() | ||||
| 		) | ||||
| 
 | ||||
| 		if reflect.ValueOf(field.Serializer).Kind() == reflect.Ptr { | ||||
| 			sameElemType = field.FieldType == reflect.ValueOf(field.Serializer).Type().Elem() | ||||
| 		} | ||||
| 
 | ||||
| 		field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { | ||||
| 			if s, ok := v.(*serializer); ok { | ||||
| 				if err = s.Serializer.Scan(ctx, field, value, s.value); err == nil { | ||||
| 					if sameElemType { | ||||
| 						field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(s.Serializer).Elem()) | ||||
| 						s.Serializer = reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface) | ||||
| 					} else if sameType { | ||||
| 						field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(s.Serializer)) | ||||
| 						s.Serializer = reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface) | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				err = oldFieldSetter(ctx, value, v) | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| package schema_test | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"database/sql" | ||||
| 	"reflect" | ||||
| 	"sync" | ||||
| @ -57,7 +58,7 @@ func TestFieldValuerAndSetter(t *testing.T) { | ||||
| 	} | ||||
| 
 | ||||
| 	for k, v := range newValues { | ||||
| 		if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil { | ||||
| 		if err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil { | ||||
| 			t.Errorf("no error should happen when assign value to field %v, but got %v", k, err) | ||||
| 		} | ||||
| 	} | ||||
| @ -80,7 +81,7 @@ func TestFieldValuerAndSetter(t *testing.T) { | ||||
| 	} | ||||
| 
 | ||||
| 	for k, v := range newValues2 { | ||||
| 		if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil { | ||||
| 		if err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil { | ||||
| 			t.Errorf("no error should happen when assign value to field %v, but got %v", k, err) | ||||
| 		} | ||||
| 	} | ||||
| @ -132,7 +133,7 @@ func TestPointerFieldValuerAndSetter(t *testing.T) { | ||||
| 	} | ||||
| 
 | ||||
| 	for k, v := range newValues { | ||||
| 		if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil { | ||||
| 		if err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil { | ||||
| 			t.Errorf("no error should happen when assign value to field %v, but got %v", k, err) | ||||
| 		} | ||||
| 	} | ||||
| @ -151,7 +152,7 @@ func TestPointerFieldValuerAndSetter(t *testing.T) { | ||||
| 	} | ||||
| 
 | ||||
| 	for k, v := range newValues2 { | ||||
| 		if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil { | ||||
| 		if err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil { | ||||
| 			t.Errorf("no error should happen when assign value to field %v, but got %v", k, err) | ||||
| 		} | ||||
| 	} | ||||
| @ -202,7 +203,7 @@ func TestAdvancedDataTypeValuerAndSetter(t *testing.T) { | ||||
| 	} | ||||
| 
 | ||||
| 	for k, v := range newValues { | ||||
| 		if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil { | ||||
| 		if err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil { | ||||
| 			t.Errorf("no error should happen when assign value to field %v, but got %v", k, err) | ||||
| 		} | ||||
| 	} | ||||
| @ -219,7 +220,7 @@ func TestAdvancedDataTypeValuerAndSetter(t *testing.T) { | ||||
| 	} | ||||
| 
 | ||||
| 	for k, v := range newValues2 { | ||||
| 		if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil { | ||||
| 		if err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil { | ||||
| 			t.Errorf("no error should happen when assign value to field %v, but got %v", k, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -4,22 +4,33 @@ import ( | ||||
| 	"gorm.io/gorm/clause" | ||||
| ) | ||||
| 
 | ||||
| // GormDataTypeInterface gorm data type interface
 | ||||
| type GormDataTypeInterface interface { | ||||
| 	GormDataType() string | ||||
| } | ||||
| 
 | ||||
| // FieldNewValuePool field new scan value pool
 | ||||
| type FieldNewValuePool interface { | ||||
| 	Get() interface{} | ||||
| 	Put(interface{}) | ||||
| } | ||||
| 
 | ||||
| // CreateClausesInterface create clauses interface
 | ||||
| type CreateClausesInterface interface { | ||||
| 	CreateClauses(*Field) []clause.Interface | ||||
| } | ||||
| 
 | ||||
| // QueryClausesInterface query clauses interface
 | ||||
| type QueryClausesInterface interface { | ||||
| 	QueryClauses(*Field) []clause.Interface | ||||
| } | ||||
| 
 | ||||
| // UpdateClausesInterface update clauses interface
 | ||||
| type UpdateClausesInterface interface { | ||||
| 	UpdateClauses(*Field) []clause.Interface | ||||
| } | ||||
| 
 | ||||
| // DeleteClausesInterface delete clauses interface
 | ||||
| type DeleteClausesInterface interface { | ||||
| 	DeleteClauses(*Field) []clause.Interface | ||||
| } | ||||
|  | ||||
							
								
								
									
										62
									
								
								schema/pool.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								schema/pool.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | ||||
| package schema | ||||
| 
 | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // sync pools
 | ||||
| var ( | ||||
| 	normalPool sync.Map | ||||
| 	stringPool = &sync.Pool{ | ||||
| 		New: func() interface{} { | ||||
| 			var v string | ||||
| 			ptrV := &v | ||||
| 			return &ptrV | ||||
| 		}, | ||||
| 	} | ||||
| 	intPool = &sync.Pool{ | ||||
| 		New: func() interface{} { | ||||
| 			var v int64 | ||||
| 			ptrV := &v | ||||
| 			return &ptrV | ||||
| 		}, | ||||
| 	} | ||||
| 	uintPool = &sync.Pool{ | ||||
| 		New: func() interface{} { | ||||
| 			var v uint64 | ||||
| 			ptrV := &v | ||||
| 			return &ptrV | ||||
| 		}, | ||||
| 	} | ||||
| 	floatPool = &sync.Pool{ | ||||
| 		New: func() interface{} { | ||||
| 			var v float64 | ||||
| 			ptrV := &v | ||||
| 			return &ptrV | ||||
| 		}, | ||||
| 	} | ||||
| 	boolPool = &sync.Pool{ | ||||
| 		New: func() interface{} { | ||||
| 			var v bool | ||||
| 			ptrV := &v | ||||
| 			return &ptrV | ||||
| 		}, | ||||
| 	} | ||||
| 	timePool = &sync.Pool{ | ||||
| 		New: func() interface{} { | ||||
| 			var v time.Time | ||||
| 			ptrV := &v | ||||
| 			return &ptrV | ||||
| 		}, | ||||
| 	} | ||||
| 	poolInitializer = func(reflectType reflect.Type) FieldNewValuePool { | ||||
| 		v, _ := normalPool.LoadOrStore(reflectType, &sync.Pool{ | ||||
| 			New: func() interface{} { | ||||
| 				return reflect.New(reflectType).Interface() | ||||
| 			}, | ||||
| 		}) | ||||
| 		return v.(FieldNewValuePool) | ||||
| 	} | ||||
| ) | ||||
| @ -1,6 +1,7 @@ | ||||
| package schema | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
| @ -576,7 +577,7 @@ func (rel *Relationship) ParseConstraint() *Constraint { | ||||
| 	return &constraint | ||||
| } | ||||
| 
 | ||||
| func (rel *Relationship) ToQueryConditions(reflectValue reflect.Value) (conds []clause.Expression) { | ||||
| func (rel *Relationship) ToQueryConditions(ctx context.Context, reflectValue reflect.Value) (conds []clause.Expression) { | ||||
| 	table := rel.FieldSchema.Table | ||||
| 	foreignFields := []*Field{} | ||||
| 	relForeignKeys := []string{} | ||||
| @ -616,7 +617,7 @@ func (rel *Relationship) ToQueryConditions(reflectValue reflect.Value) (conds [] | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	_, foreignValues := GetIdentityFieldValuesMap(reflectValue, foreignFields) | ||||
| 	_, foreignValues := GetIdentityFieldValuesMap(ctx, reflectValue, foreignFields) | ||||
| 	column, values := ToQueryValues(table, relForeignKeys, foreignValues) | ||||
| 
 | ||||
| 	conds = append(conds, clause.IN{Column: column, Values: values}) | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| package schema_test | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
| @ -203,7 +204,7 @@ func checkSchemaRelation(t *testing.T, s *schema.Schema, relation Relation) { | ||||
| func checkField(t *testing.T, s *schema.Schema, value reflect.Value, values map[string]interface{}) { | ||||
| 	for k, v := range values { | ||||
| 		t.Run("CheckField/"+k, func(t *testing.T) { | ||||
| 			fv, _ := s.FieldsByDBName[k].ValueOf(value) | ||||
| 			fv, _ := s.FieldsByDBName[k].ValueOf(context.Background(), value) | ||||
| 			tests.AssertEqual(t, v, fv) | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
							
								
								
									
										125
									
								
								schema/serializer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								schema/serializer.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,125 @@ | ||||
| package schema | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"database/sql" | ||||
| 	"database/sql/driver" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| var serializerMap = sync.Map{} | ||||
| 
 | ||||
| // RegisterSerializer register serializer
 | ||||
| func RegisterSerializer(name string, serializer SerializerInterface) { | ||||
| 	serializerMap.Store(strings.ToLower(name), serializer) | ||||
| } | ||||
| 
 | ||||
| // GetSerializer get serializer
 | ||||
| func GetSerializer(name string) (serializer SerializerInterface, ok bool) { | ||||
| 	v, ok := serializerMap.Load(strings.ToLower(name)) | ||||
| 	if ok { | ||||
| 		serializer, ok = v.(SerializerInterface) | ||||
| 	} | ||||
| 	return serializer, ok | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	RegisterSerializer("json", JSONSerializer{}) | ||||
| 	RegisterSerializer("unixtime", UnixSecondSerializer{}) | ||||
| } | ||||
| 
 | ||||
| // Serializer field value serializer
 | ||||
| type serializer struct { | ||||
| 	Field           *Field | ||||
| 	Serializer      SerializerInterface | ||||
| 	SerializeValuer SerializerValuerInterface | ||||
| 	Destination     reflect.Value | ||||
| 	Context         context.Context | ||||
| 	value           interface{} | ||||
| 	fieldValue      interface{} | ||||
| } | ||||
| 
 | ||||
| // Scan implements sql.Scanner interface
 | ||||
| func (s *serializer) Scan(value interface{}) error { | ||||
| 	s.value = value | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Value implements driver.Valuer interface
 | ||||
| func (s serializer) Value() (driver.Value, error) { | ||||
| 	return s.SerializeValuer.Value(s.Context, s.Field, s.Destination, s.fieldValue) | ||||
| } | ||||
| 
 | ||||
| // SerializerInterface serializer interface
 | ||||
| type SerializerInterface interface { | ||||
| 	Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) error | ||||
| 	SerializerValuerInterface | ||||
| } | ||||
| 
 | ||||
| // SerializerValuerInterface serializer valuer interface
 | ||||
| type SerializerValuerInterface interface { | ||||
| 	Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) | ||||
| } | ||||
| 
 | ||||
| // JSONSerializer json serializer
 | ||||
| type JSONSerializer struct { | ||||
| } | ||||
| 
 | ||||
| // Scan implements serializer interface
 | ||||
| func (JSONSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) (err error) { | ||||
| 	fieldValue := reflect.New(field.FieldType) | ||||
| 
 | ||||
| 	if dbValue != nil { | ||||
| 		var bytes []byte | ||||
| 		switch v := dbValue.(type) { | ||||
| 		case []byte: | ||||
| 			bytes = v | ||||
| 		case string: | ||||
| 			bytes = []byte(v) | ||||
| 		default: | ||||
| 			return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", dbValue)) | ||||
| 		} | ||||
| 
 | ||||
| 		err = json.Unmarshal(bytes, fieldValue.Interface()) | ||||
| 	} | ||||
| 
 | ||||
| 	field.ReflectValueOf(ctx, dst).Set(fieldValue.Elem()) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Value implements serializer interface
 | ||||
| func (JSONSerializer) Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) { | ||||
| 	result, err := json.Marshal(fieldValue) | ||||
| 	return string(result), err | ||||
| } | ||||
| 
 | ||||
| // UnixSecondSerializer json serializer
 | ||||
| type UnixSecondSerializer struct { | ||||
| } | ||||
| 
 | ||||
| // Scan implements serializer interface
 | ||||
| func (UnixSecondSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) (err error) { | ||||
| 	t := sql.NullTime{} | ||||
| 	if err = t.Scan(dbValue); err == nil { | ||||
| 		err = field.Set(ctx, dst, t.Time) | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Value implements serializer interface
 | ||||
| func (UnixSecondSerializer) Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (result interface{}, err error) { | ||||
| 	switch v := fieldValue.(type) { | ||||
| 	case int64, int, uint, uint64, int32, uint32, int16, uint16: | ||||
| 		result = time.Unix(reflect.ValueOf(v).Int(), 0) | ||||
| 	default: | ||||
| 		err = fmt.Errorf("invalid field type %#v for UnixSecondSerializer, only int, uint supported", v) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| @ -1,6 +1,7 @@ | ||||
| package schema | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"reflect" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| @ -59,13 +60,13 @@ func removeSettingFromTag(tag reflect.StructTag, names ...string) reflect.Struct | ||||
| } | ||||
| 
 | ||||
| // GetRelationsValues get relations's values from a reflect value
 | ||||
| func GetRelationsValues(reflectValue reflect.Value, rels []*Relationship) (reflectResults reflect.Value) { | ||||
| func GetRelationsValues(ctx context.Context, reflectValue reflect.Value, rels []*Relationship) (reflectResults reflect.Value) { | ||||
| 	for _, rel := range rels { | ||||
| 		reflectResults = reflect.MakeSlice(reflect.SliceOf(reflect.PtrTo(rel.FieldSchema.ModelType)), 0, 1) | ||||
| 
 | ||||
| 		appendToResults := func(value reflect.Value) { | ||||
| 			if _, isZero := rel.Field.ValueOf(value); !isZero { | ||||
| 				result := reflect.Indirect(rel.Field.ReflectValueOf(value)) | ||||
| 			if _, isZero := rel.Field.ValueOf(ctx, value); !isZero { | ||||
| 				result := reflect.Indirect(rel.Field.ReflectValueOf(ctx, value)) | ||||
| 				switch result.Kind() { | ||||
| 				case reflect.Struct: | ||||
| 					reflectResults = reflect.Append(reflectResults, result.Addr()) | ||||
| @ -97,7 +98,7 @@ func GetRelationsValues(reflectValue reflect.Value, rels []*Relationship) (refle | ||||
| } | ||||
| 
 | ||||
| // GetIdentityFieldValuesMap get identity map from fields
 | ||||
| func GetIdentityFieldValuesMap(reflectValue reflect.Value, fields []*Field) (map[string][]reflect.Value, [][]interface{}) { | ||||
| func GetIdentityFieldValuesMap(ctx context.Context, reflectValue reflect.Value, fields []*Field) (map[string][]reflect.Value, [][]interface{}) { | ||||
| 	var ( | ||||
| 		results       = [][]interface{}{} | ||||
| 		dataResults   = map[string][]reflect.Value{} | ||||
| @ -110,7 +111,7 @@ func GetIdentityFieldValuesMap(reflectValue reflect.Value, fields []*Field) (map | ||||
| 		results = [][]interface{}{make([]interface{}, len(fields))} | ||||
| 
 | ||||
| 		for idx, field := range fields { | ||||
| 			results[0][idx], zero = field.ValueOf(reflectValue) | ||||
| 			results[0][idx], zero = field.ValueOf(ctx, reflectValue) | ||||
| 			notZero = notZero || !zero | ||||
| 		} | ||||
| 
 | ||||
| @ -135,7 +136,7 @@ func GetIdentityFieldValuesMap(reflectValue reflect.Value, fields []*Field) (map | ||||
| 			fieldValues := make([]interface{}, len(fields)) | ||||
| 			notZero = false | ||||
| 			for idx, field := range fields { | ||||
| 				fieldValues[idx], zero = field.ValueOf(elem) | ||||
| 				fieldValues[idx], zero = field.ValueOf(ctx, elem) | ||||
| 				notZero = notZero || !zero | ||||
| 			} | ||||
| 
 | ||||
| @ -155,12 +156,12 @@ func GetIdentityFieldValuesMap(reflectValue reflect.Value, fields []*Field) (map | ||||
| } | ||||
| 
 | ||||
| // GetIdentityFieldValuesMapFromValues get identity map from fields
 | ||||
| func GetIdentityFieldValuesMapFromValues(values []interface{}, fields []*Field) (map[string][]reflect.Value, [][]interface{}) { | ||||
| func GetIdentityFieldValuesMapFromValues(ctx context.Context, values []interface{}, fields []*Field) (map[string][]reflect.Value, [][]interface{}) { | ||||
| 	resultsMap := map[string][]reflect.Value{} | ||||
| 	results := [][]interface{}{} | ||||
| 
 | ||||
| 	for _, v := range values { | ||||
| 		rm, rs := GetIdentityFieldValuesMap(reflect.Indirect(reflect.ValueOf(v)), fields) | ||||
| 		rm, rs := GetIdentityFieldValuesMap(ctx, reflect.Indirect(reflect.ValueOf(v)), fields) | ||||
| 		for k, v := range rm { | ||||
| 			resultsMap[k] = append(resultsMap[k], v...) | ||||
| 		} | ||||
|  | ||||
| @ -135,7 +135,7 @@ func (sd SoftDeleteDeleteClause) ModifyStatement(stmt *Statement) { | ||||
| 		stmt.SetColumn(sd.Field.DBName, curTime, true) | ||||
| 
 | ||||
| 		if stmt.Schema != nil { | ||||
| 			_, queryValues := schema.GetIdentityFieldValuesMap(stmt.ReflectValue, stmt.Schema.PrimaryFields) | ||||
| 			_, queryValues := schema.GetIdentityFieldValuesMap(stmt.Context, stmt.ReflectValue, stmt.Schema.PrimaryFields) | ||||
| 			column, values := schema.ToQueryValues(stmt.Table, stmt.Schema.PrimaryFieldDBNames, queryValues) | ||||
| 
 | ||||
| 			if len(values) > 0 { | ||||
| @ -143,7 +143,7 @@ func (sd SoftDeleteDeleteClause) ModifyStatement(stmt *Statement) { | ||||
| 			} | ||||
| 
 | ||||
| 			if stmt.ReflectValue.CanAddr() && stmt.Dest != stmt.Model && stmt.Model != nil { | ||||
| 				_, queryValues = schema.GetIdentityFieldValuesMap(reflect.ValueOf(stmt.Model), stmt.Schema.PrimaryFields) | ||||
| 				_, queryValues = schema.GetIdentityFieldValuesMap(stmt.Context, reflect.ValueOf(stmt.Model), stmt.Schema.PrimaryFields) | ||||
| 				column, values = schema.ToQueryValues(stmt.Table, stmt.Schema.PrimaryFieldDBNames, queryValues) | ||||
| 
 | ||||
| 				if len(values) > 0 { | ||||
|  | ||||
							
								
								
									
										16
									
								
								statement.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								statement.go
									
									
									
									
									
								
							| @ -389,7 +389,7 @@ func (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) [] | ||||
| 					for _, field := range s.Fields { | ||||
| 						selected := selectedColumns[field.DBName] || selectedColumns[field.Name] | ||||
| 						if selected || (!restricted && field.Readable) { | ||||
| 							if v, isZero := field.ValueOf(reflectValue); !isZero || selected { | ||||
| 							if v, isZero := field.ValueOf(stmt.Context, reflectValue); !isZero || selected { | ||||
| 								if field.DBName != "" { | ||||
| 									conds = append(conds, clause.Eq{Column: clause.Column{Table: clause.CurrentTable, Name: field.DBName}, Value: v}) | ||||
| 								} else if field.DataType != "" { | ||||
| @ -403,7 +403,7 @@ func (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) [] | ||||
| 						for _, field := range s.Fields { | ||||
| 							selected := selectedColumns[field.DBName] || selectedColumns[field.Name] | ||||
| 							if selected || (!restricted && field.Readable) { | ||||
| 								if v, isZero := field.ValueOf(reflectValue.Index(i)); !isZero || selected { | ||||
| 								if v, isZero := field.ValueOf(stmt.Context, reflectValue.Index(i)); !isZero || selected { | ||||
| 									if field.DBName != "" { | ||||
| 										conds = append(conds, clause.Eq{Column: clause.Column{Table: clause.CurrentTable, Name: field.DBName}, Value: v}) | ||||
| 									} else if field.DataType != "" { | ||||
| @ -562,7 +562,7 @@ func (stmt *Statement) SetColumn(name string, value interface{}, fromCallbacks . | ||||
| 
 | ||||
| 				switch destValue.Kind() { | ||||
| 				case reflect.Struct: | ||||
| 					field.Set(destValue, value) | ||||
| 					field.Set(stmt.Context, destValue, value) | ||||
| 				default: | ||||
| 					stmt.AddError(ErrInvalidData) | ||||
| 				} | ||||
| @ -572,10 +572,10 @@ func (stmt *Statement) SetColumn(name string, value interface{}, fromCallbacks . | ||||
| 			case reflect.Slice, reflect.Array: | ||||
| 				if len(fromCallbacks) > 0 { | ||||
| 					for i := 0; i < stmt.ReflectValue.Len(); i++ { | ||||
| 						field.Set(stmt.ReflectValue.Index(i), value) | ||||
| 						field.Set(stmt.Context, stmt.ReflectValue.Index(i), value) | ||||
| 					} | ||||
| 				} else { | ||||
| 					field.Set(stmt.ReflectValue.Index(stmt.CurDestIndex), value) | ||||
| 					field.Set(stmt.Context, stmt.ReflectValue.Index(stmt.CurDestIndex), value) | ||||
| 				} | ||||
| 			case reflect.Struct: | ||||
| 				if !stmt.ReflectValue.CanAddr() { | ||||
| @ -583,7 +583,7 @@ func (stmt *Statement) SetColumn(name string, value interface{}, fromCallbacks . | ||||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				field.Set(stmt.ReflectValue, value) | ||||
| 				field.Set(stmt.Context, stmt.ReflectValue, value) | ||||
| 			} | ||||
| 		} else { | ||||
| 			stmt.AddError(ErrInvalidField) | ||||
| @ -603,7 +603,7 @@ func (stmt *Statement) Changed(fields ...string) bool { | ||||
| 
 | ||||
| 	selectColumns, restricted := stmt.SelectAndOmitColumns(false, true) | ||||
| 	changed := func(field *schema.Field) bool { | ||||
| 		fieldValue, _ := field.ValueOf(modelValue) | ||||
| 		fieldValue, _ := field.ValueOf(stmt.Context, modelValue) | ||||
| 		if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) { | ||||
| 			if v, ok := stmt.Dest.(map[string]interface{}); ok { | ||||
| 				if fv, ok := v[field.Name]; ok { | ||||
| @ -617,7 +617,7 @@ func (stmt *Statement) Changed(fields ...string) bool { | ||||
| 					destValue = destValue.Elem() | ||||
| 				} | ||||
| 
 | ||||
| 				changedValue, zero := field.ValueOf(destValue) | ||||
| 				changedValue, zero := field.ValueOf(stmt.Context, destValue) | ||||
| 				return !zero && !utils.AssertEqual(changedValue, fieldValue) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @ -123,7 +123,7 @@ func TestCreateFromMap(t *testing.T) { | ||||
| 		{"name": "create_from_map_3", "Age": 20}, | ||||
| 	} | ||||
| 
 | ||||
| 	if err := DB.Model(&User{}).Create(datas).Error; err != nil { | ||||
| 	if err := DB.Model(&User{}).Create(&datas).Error; err != nil { | ||||
| 		t.Fatalf("failed to create data from slice of map, got error: %v", err) | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -9,7 +9,7 @@ require ( | ||||
| 	github.com/jinzhu/now v1.1.4 | ||||
| 	github.com/lib/pq v1.10.4 | ||||
| 	github.com/mattn/go-sqlite3 v1.14.11 // indirect | ||||
| 	golang.org/x/crypto v0.0.0-20220208233918-bba287dce954 // indirect | ||||
| 	golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect | ||||
| 	gorm.io/driver/mysql v1.2.3 | ||||
| 	gorm.io/driver/postgres v1.2.3 | ||||
| 	gorm.io/driver/sqlite v1.2.6 | ||||
|  | ||||
							
								
								
									
										71
									
								
								tests/serializer_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								tests/serializer_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| package tests_test | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"gorm.io/gorm" | ||||
| 	"gorm.io/gorm/schema" | ||||
| 	. "gorm.io/gorm/utils/tests" | ||||
| ) | ||||
| 
 | ||||
| type SerializerStruct struct { | ||||
| 	gorm.Model | ||||
| 	Name            []byte                 `gorm:"json"` | ||||
| 	Roles           Roles                  `gorm:"serializer:json"` | ||||
| 	Contracts       map[string]interface{} `gorm:"serializer:json"` | ||||
| 	CreatedTime     int64                  `gorm:"serializer:unixtime;type:time"` // store time in db, use int as field type
 | ||||
| 	EncryptedString EncryptedString | ||||
| } | ||||
| 
 | ||||
| type Roles []string | ||||
| type EncryptedString string | ||||
| 
 | ||||
| func (es *EncryptedString) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) (err error) { | ||||
| 	switch value := dbValue.(type) { | ||||
| 	case []byte: | ||||
| 		*es = EncryptedString(bytes.TrimPrefix(value, []byte("hello"))) | ||||
| 	case string: | ||||
| 		*es = EncryptedString(strings.TrimPrefix(value, "hello")) | ||||
| 	default: | ||||
| 		return fmt.Errorf("unsupported data %v", dbValue) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (es EncryptedString) Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) { | ||||
| 	return "hello" + string(es), nil | ||||
| } | ||||
| 
 | ||||
| func TestSerializer(t *testing.T) { | ||||
| 	DB.Migrator().DropTable(&SerializerStruct{}) | ||||
| 	if err := DB.Migrator().AutoMigrate(&SerializerStruct{}); err != nil { | ||||
| 		t.Fatalf("no error should happen when migrate scanner, valuer struct, got error %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	createdAt := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) | ||||
| 
 | ||||
| 	data := SerializerStruct{ | ||||
| 		Name:            []byte("jinzhu"), | ||||
| 		Roles:           []string{"r1", "r2"}, | ||||
| 		Contracts:       map[string]interface{}{"name": "jinzhu", "age": 10}, | ||||
| 		EncryptedString: EncryptedString("pass"), | ||||
| 		CreatedTime:     createdAt.Unix(), | ||||
| 	} | ||||
| 
 | ||||
| 	if err := DB.Create(&data).Error; err != nil { | ||||
| 		t.Fatalf("failed to create data, got error %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	var result SerializerStruct | ||||
| 	if err := DB.First(&result, data.ID).Error; err != nil { | ||||
| 		t.Fatalf("failed to query data, got error %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	AssertEqual(t, result, data) | ||||
| } | ||||
| @ -36,17 +36,14 @@ func IsValidDBNameChar(c rune) bool { | ||||
| 	return !unicode.IsLetter(c) && !unicode.IsNumber(c) && c != '.' && c != '*' && c != '_' && c != '$' && c != '@' | ||||
| } | ||||
| 
 | ||||
| func CheckTruth(val interface{}) bool { | ||||
| 	if v, ok := val.(bool); ok { | ||||
| 		return v | ||||
| // CheckTruth check string true or not
 | ||||
| func CheckTruth(vals ...string) bool { | ||||
| 	for _, val := range vals { | ||||
| 		if !strings.EqualFold(val, "false") && val != "" { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if v, ok := val.(string); ok { | ||||
| 		v = strings.ToLower(v) | ||||
| 		return v != "false" | ||||
| 	} | ||||
| 
 | ||||
| 	return !reflect.ValueOf(val).IsZero() | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func ToStringKey(values ...interface{}) string { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Jinzhu
						Jinzhu