mirror of https://github.com/go-gorm/gorm.git
lock TagSettings structure when modified (#1796)
The map is modified in different places in the code which results in race conditions on execution. This commit locks the map with read-write lock when it is modified
This commit is contained in:
parent
282f11af19
commit
123d4f50ef
|
@ -100,7 +100,7 @@ func autoPreload(scope *Scope) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := field.TagSettings["PRELOAD"]; ok {
|
if val, ok := field.TagSettingsGet("PRELOAD"); ok {
|
||||||
if preload, err := strconv.ParseBool(val); err != nil {
|
if preload, err := strconv.ParseBool(val); err != nil {
|
||||||
scope.Err(errors.New("invalid preload option"))
|
scope.Err(errors.New("invalid preload option"))
|
||||||
return
|
return
|
||||||
|
|
|
@ -35,7 +35,7 @@ func saveAssociationCheck(scope *Scope, field *Field) (autoUpdate bool, autoCrea
|
||||||
autoUpdate = checkTruth(value)
|
autoUpdate = checkTruth(value)
|
||||||
autoCreate = autoUpdate
|
autoCreate = autoUpdate
|
||||||
saveReference = autoUpdate
|
saveReference = autoUpdate
|
||||||
} else if value, ok := field.TagSettings["SAVE_ASSOCIATIONS"]; ok {
|
} else if value, ok := field.TagSettingsGet("SAVE_ASSOCIATIONS"); ok {
|
||||||
autoUpdate = checkTruth(value)
|
autoUpdate = checkTruth(value)
|
||||||
autoCreate = autoUpdate
|
autoCreate = autoUpdate
|
||||||
saveReference = autoUpdate
|
saveReference = autoUpdate
|
||||||
|
@ -43,19 +43,19 @@ func saveAssociationCheck(scope *Scope, field *Field) (autoUpdate bool, autoCrea
|
||||||
|
|
||||||
if value, ok := scope.Get("gorm:association_autoupdate"); ok {
|
if value, ok := scope.Get("gorm:association_autoupdate"); ok {
|
||||||
autoUpdate = checkTruth(value)
|
autoUpdate = checkTruth(value)
|
||||||
} else if value, ok := field.TagSettings["ASSOCIATION_AUTOUPDATE"]; ok {
|
} else if value, ok := field.TagSettingsGet("ASSOCIATION_AUTOUPDATE"); ok {
|
||||||
autoUpdate = checkTruth(value)
|
autoUpdate = checkTruth(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if value, ok := scope.Get("gorm:association_autocreate"); ok {
|
if value, ok := scope.Get("gorm:association_autocreate"); ok {
|
||||||
autoCreate = checkTruth(value)
|
autoCreate = checkTruth(value)
|
||||||
} else if value, ok := field.TagSettings["ASSOCIATION_AUTOCREATE"]; ok {
|
} else if value, ok := field.TagSettingsGet("ASSOCIATION_AUTOCREATE"); ok {
|
||||||
autoCreate = checkTruth(value)
|
autoCreate = checkTruth(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if value, ok := scope.Get("gorm:association_save_reference"); ok {
|
if value, ok := scope.Get("gorm:association_save_reference"); ok {
|
||||||
saveReference = checkTruth(value)
|
saveReference = checkTruth(value)
|
||||||
} else if value, ok := field.TagSettings["ASSOCIATION_SAVE_REFERENCE"]; ok {
|
} else if value, ok := field.TagSettingsGet("ASSOCIATION_SAVE_REFERENCE"); ok {
|
||||||
saveReference = checkTruth(value)
|
saveReference = checkTruth(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10
dialect.go
10
dialect.go
|
@ -83,7 +83,7 @@ var ParseFieldStructForDialect = func(field *StructField, dialect Dialect) (fiel
|
||||||
// Get redirected field type
|
// Get redirected field type
|
||||||
var (
|
var (
|
||||||
reflectType = field.Struct.Type
|
reflectType = field.Struct.Type
|
||||||
dataType = field.TagSettings["TYPE"]
|
dataType, _ = field.TagSettingsGet("TYPE")
|
||||||
)
|
)
|
||||||
|
|
||||||
for reflectType.Kind() == reflect.Ptr {
|
for reflectType.Kind() == reflect.Ptr {
|
||||||
|
@ -112,15 +112,17 @@ var ParseFieldStructForDialect = func(field *StructField, dialect Dialect) (fiel
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default Size
|
// Default Size
|
||||||
if num, ok := field.TagSettings["SIZE"]; ok {
|
if num, ok := field.TagSettingsGet("SIZE"); ok {
|
||||||
size, _ = strconv.Atoi(num)
|
size, _ = strconv.Atoi(num)
|
||||||
} else {
|
} else {
|
||||||
size = 255
|
size = 255
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default type from tag setting
|
// Default type from tag setting
|
||||||
additionalType = field.TagSettings["NOT NULL"] + " " + field.TagSettings["UNIQUE"]
|
notNull, _ := field.TagSettingsGet("NOT NULL")
|
||||||
if value, ok := field.TagSettings["DEFAULT"]; ok {
|
unique, _ := field.TagSettingsGet("UNIQUE")
|
||||||
|
additionalType = notNull + " " + unique
|
||||||
|
if value, ok := field.TagSettingsGet("DEFAULT"); ok {
|
||||||
additionalType = additionalType + " DEFAULT " + value
|
additionalType = additionalType + " DEFAULT " + value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ func (commonDialect) Quote(key string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *commonDialect) fieldCanAutoIncrement(field *StructField) bool {
|
func (s *commonDialect) fieldCanAutoIncrement(field *StructField) bool {
|
||||||
if value, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
|
if value, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok {
|
||||||
return strings.ToLower(value) != "false"
|
return strings.ToLower(value) != "false"
|
||||||
}
|
}
|
||||||
return field.IsPrimaryKey
|
return field.IsPrimaryKey
|
||||||
|
|
|
@ -33,9 +33,9 @@ func (s *mysql) DataTypeOf(field *StructField) string {
|
||||||
|
|
||||||
// MySQL allows only one auto increment column per table, and it must
|
// MySQL allows only one auto increment column per table, and it must
|
||||||
// be a KEY column.
|
// be a KEY column.
|
||||||
if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
|
if _, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok {
|
||||||
if _, ok = field.TagSettings["INDEX"]; !ok && !field.IsPrimaryKey {
|
if _, ok = field.TagSettingsGet("INDEX"); !ok && !field.IsPrimaryKey {
|
||||||
delete(field.TagSettings, "AUTO_INCREMENT")
|
field.TagSettingsDelete("AUTO_INCREMENT")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,42 +45,42 @@ func (s *mysql) DataTypeOf(field *StructField) string {
|
||||||
sqlType = "boolean"
|
sqlType = "boolean"
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
if s.fieldCanAutoIncrement(field) {
|
if s.fieldCanAutoIncrement(field) {
|
||||||
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
|
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
|
||||||
sqlType = "tinyint AUTO_INCREMENT"
|
sqlType = "tinyint AUTO_INCREMENT"
|
||||||
} else {
|
} else {
|
||||||
sqlType = "tinyint"
|
sqlType = "tinyint"
|
||||||
}
|
}
|
||||||
case reflect.Int, reflect.Int16, reflect.Int32:
|
case reflect.Int, reflect.Int16, reflect.Int32:
|
||||||
if s.fieldCanAutoIncrement(field) {
|
if s.fieldCanAutoIncrement(field) {
|
||||||
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
|
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
|
||||||
sqlType = "int AUTO_INCREMENT"
|
sqlType = "int AUTO_INCREMENT"
|
||||||
} else {
|
} else {
|
||||||
sqlType = "int"
|
sqlType = "int"
|
||||||
}
|
}
|
||||||
case reflect.Uint8:
|
case reflect.Uint8:
|
||||||
if s.fieldCanAutoIncrement(field) {
|
if s.fieldCanAutoIncrement(field) {
|
||||||
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
|
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
|
||||||
sqlType = "tinyint unsigned AUTO_INCREMENT"
|
sqlType = "tinyint unsigned AUTO_INCREMENT"
|
||||||
} else {
|
} else {
|
||||||
sqlType = "tinyint unsigned"
|
sqlType = "tinyint unsigned"
|
||||||
}
|
}
|
||||||
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
|
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
|
||||||
if s.fieldCanAutoIncrement(field) {
|
if s.fieldCanAutoIncrement(field) {
|
||||||
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
|
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
|
||||||
sqlType = "int unsigned AUTO_INCREMENT"
|
sqlType = "int unsigned AUTO_INCREMENT"
|
||||||
} else {
|
} else {
|
||||||
sqlType = "int unsigned"
|
sqlType = "int unsigned"
|
||||||
}
|
}
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
if s.fieldCanAutoIncrement(field) {
|
if s.fieldCanAutoIncrement(field) {
|
||||||
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
|
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
|
||||||
sqlType = "bigint AUTO_INCREMENT"
|
sqlType = "bigint AUTO_INCREMENT"
|
||||||
} else {
|
} else {
|
||||||
sqlType = "bigint"
|
sqlType = "bigint"
|
||||||
}
|
}
|
||||||
case reflect.Uint64:
|
case reflect.Uint64:
|
||||||
if s.fieldCanAutoIncrement(field) {
|
if s.fieldCanAutoIncrement(field) {
|
||||||
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
|
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
|
||||||
sqlType = "bigint unsigned AUTO_INCREMENT"
|
sqlType = "bigint unsigned AUTO_INCREMENT"
|
||||||
} else {
|
} else {
|
||||||
sqlType = "bigint unsigned"
|
sqlType = "bigint unsigned"
|
||||||
|
@ -96,11 +96,11 @@ func (s *mysql) DataTypeOf(field *StructField) string {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
if _, ok := dataValue.Interface().(time.Time); ok {
|
if _, ok := dataValue.Interface().(time.Time); ok {
|
||||||
precision := ""
|
precision := ""
|
||||||
if p, ok := field.TagSettings["PRECISION"]; ok {
|
if p, ok := field.TagSettingsGet("PRECISION"); ok {
|
||||||
precision = fmt.Sprintf("(%s)", p)
|
precision = fmt.Sprintf("(%s)", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := field.TagSettings["NOT NULL"]; ok {
|
if _, ok := field.TagSettingsGet("NOT NULL"); ok {
|
||||||
sqlType = fmt.Sprintf("timestamp%v", precision)
|
sqlType = fmt.Sprintf("timestamp%v", precision)
|
||||||
} else {
|
} else {
|
||||||
sqlType = fmt.Sprintf("timestamp%v NULL", precision)
|
sqlType = fmt.Sprintf("timestamp%v NULL", precision)
|
||||||
|
|
|
@ -34,14 +34,14 @@ func (s *postgres) DataTypeOf(field *StructField) string {
|
||||||
sqlType = "boolean"
|
sqlType = "boolean"
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uintptr:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uintptr:
|
||||||
if s.fieldCanAutoIncrement(field) {
|
if s.fieldCanAutoIncrement(field) {
|
||||||
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
|
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
|
||||||
sqlType = "serial"
|
sqlType = "serial"
|
||||||
} else {
|
} else {
|
||||||
sqlType = "integer"
|
sqlType = "integer"
|
||||||
}
|
}
|
||||||
case reflect.Int64, reflect.Uint32, reflect.Uint64:
|
case reflect.Int64, reflect.Uint32, reflect.Uint64:
|
||||||
if s.fieldCanAutoIncrement(field) {
|
if s.fieldCanAutoIncrement(field) {
|
||||||
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
|
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
|
||||||
sqlType = "bigserial"
|
sqlType = "bigserial"
|
||||||
} else {
|
} else {
|
||||||
sqlType = "bigint"
|
sqlType = "bigint"
|
||||||
|
@ -49,7 +49,7 @@ func (s *postgres) DataTypeOf(field *StructField) string {
|
||||||
case reflect.Float32, reflect.Float64:
|
case reflect.Float32, reflect.Float64:
|
||||||
sqlType = "numeric"
|
sqlType = "numeric"
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
if _, ok := field.TagSettings["SIZE"]; !ok {
|
if _, ok := field.TagSettingsGet("SIZE"); !ok {
|
||||||
size = 0 // if SIZE haven't been set, use `text` as the default type, as there are no performance different
|
size = 0 // if SIZE haven't been set, use `text` as the default type, as there are no performance different
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,14 +29,14 @@ func (s *sqlite3) DataTypeOf(field *StructField) string {
|
||||||
sqlType = "bool"
|
sqlType = "bool"
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
|
||||||
if s.fieldCanAutoIncrement(field) {
|
if s.fieldCanAutoIncrement(field) {
|
||||||
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
|
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
|
||||||
sqlType = "integer primary key autoincrement"
|
sqlType = "integer primary key autoincrement"
|
||||||
} else {
|
} else {
|
||||||
sqlType = "integer"
|
sqlType = "integer"
|
||||||
}
|
}
|
||||||
case reflect.Int64, reflect.Uint64:
|
case reflect.Int64, reflect.Uint64:
|
||||||
if s.fieldCanAutoIncrement(field) {
|
if s.fieldCanAutoIncrement(field) {
|
||||||
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
|
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
|
||||||
sqlType = "integer primary key autoincrement"
|
sqlType = "integer primary key autoincrement"
|
||||||
} else {
|
} else {
|
||||||
sqlType = "bigint"
|
sqlType = "bigint"
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
func setIdentityInsert(scope *gorm.Scope) {
|
func setIdentityInsert(scope *gorm.Scope) {
|
||||||
if scope.Dialect().GetName() == "mssql" {
|
if scope.Dialect().GetName() == "mssql" {
|
||||||
for _, field := range scope.PrimaryFields() {
|
for _, field := range scope.PrimaryFields() {
|
||||||
if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok && !field.IsBlank {
|
if _, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok && !field.IsBlank {
|
||||||
scope.NewDB().Exec(fmt.Sprintf("SET IDENTITY_INSERT %v ON", scope.TableName()))
|
scope.NewDB().Exec(fmt.Sprintf("SET IDENTITY_INSERT %v ON", scope.TableName()))
|
||||||
scope.InstanceSet("mssql:identity_insert_on", true)
|
scope.InstanceSet("mssql:identity_insert_on", true)
|
||||||
}
|
}
|
||||||
|
@ -70,14 +70,14 @@ func (s *mssql) DataTypeOf(field *gorm.StructField) string {
|
||||||
sqlType = "bit"
|
sqlType = "bit"
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
|
||||||
if s.fieldCanAutoIncrement(field) {
|
if s.fieldCanAutoIncrement(field) {
|
||||||
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
|
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
|
||||||
sqlType = "int IDENTITY(1,1)"
|
sqlType = "int IDENTITY(1,1)"
|
||||||
} else {
|
} else {
|
||||||
sqlType = "int"
|
sqlType = "int"
|
||||||
}
|
}
|
||||||
case reflect.Int64, reflect.Uint64:
|
case reflect.Int64, reflect.Uint64:
|
||||||
if s.fieldCanAutoIncrement(field) {
|
if s.fieldCanAutoIncrement(field) {
|
||||||
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
|
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
|
||||||
sqlType = "bigint IDENTITY(1,1)"
|
sqlType = "bigint IDENTITY(1,1)"
|
||||||
} else {
|
} else {
|
||||||
sqlType = "bigint"
|
sqlType = "bigint"
|
||||||
|
@ -116,7 +116,7 @@ func (s *mssql) DataTypeOf(field *gorm.StructField) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s mssql) fieldCanAutoIncrement(field *gorm.StructField) bool {
|
func (s mssql) fieldCanAutoIncrement(field *gorm.StructField) bool {
|
||||||
if value, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
|
if value, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok {
|
||||||
return value != "FALSE"
|
return value != "FALSE"
|
||||||
}
|
}
|
||||||
return field.IsPrimaryKey
|
return field.IsPrimaryKey
|
||||||
|
|
|
@ -43,7 +43,7 @@ func TestCalculateField(t *testing.T) {
|
||||||
|
|
||||||
if field, ok := scope.FieldByName("embedded_name"); !ok {
|
if field, ok := scope.FieldByName("embedded_name"); !ok {
|
||||||
t.Errorf("should find embedded field")
|
t.Errorf("should find embedded field")
|
||||||
} else if _, ok := field.TagSettings["NOT NULL"]; !ok {
|
} else if _, ok := field.TagSettingsGet("NOT NULL"); !ok {
|
||||||
t.Errorf("should find embedded field's tag settings")
|
t.Errorf("should find embedded field's tag settings")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
main.go
2
main.go
|
@ -699,7 +699,7 @@ func (s *DB) SetJoinTableHandler(source interface{}, column string, handler Join
|
||||||
scope := s.NewScope(source)
|
scope := s.NewScope(source)
|
||||||
for _, field := range scope.GetModelStruct().StructFields {
|
for _, field := range scope.GetModelStruct().StructFields {
|
||||||
if field.Name == column || field.DBName == column {
|
if field.Name == column || field.DBName == column {
|
||||||
if many2many := field.TagSettings["MANY2MANY"]; many2many != "" {
|
if many2many, _ := field.TagSettingsGet("MANY2MANY"); many2many != "" {
|
||||||
source := (&Scope{Value: source}).GetModelStruct().ModelType
|
source := (&Scope{Value: source}).GetModelStruct().ModelType
|
||||||
destination := (&Scope{Value: reflect.New(field.Struct.Type).Interface()}).GetModelStruct().ModelType
|
destination := (&Scope{Value: reflect.New(field.Struct.Type).Interface()}).GetModelStruct().ModelType
|
||||||
handler.Setup(field.Relationship, many2many, source, destination)
|
handler.Setup(field.Relationship, many2many, source, destination)
|
||||||
|
|
|
@ -60,6 +60,30 @@ type StructField struct {
|
||||||
Struct reflect.StructField
|
Struct reflect.StructField
|
||||||
IsForeignKey bool
|
IsForeignKey bool
|
||||||
Relationship *Relationship
|
Relationship *Relationship
|
||||||
|
|
||||||
|
tagSettingsLock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagSettingsSet Sets a tag in the tag settings map
|
||||||
|
func (s *StructField) TagSettingsSet(key, val string) {
|
||||||
|
s.tagSettingsLock.Lock()
|
||||||
|
defer s.tagSettingsLock.Unlock()
|
||||||
|
s.TagSettings[key] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagSettingsGet returns a tag from the tag settings
|
||||||
|
func (s *StructField) TagSettingsGet(key string) (string, bool) {
|
||||||
|
s.tagSettingsLock.RLock()
|
||||||
|
defer s.tagSettingsLock.RUnlock()
|
||||||
|
val, ok := s.TagSettings[key]
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagSettingsDelete deletes a tag
|
||||||
|
func (s *StructField) TagSettingsDelete(key string) {
|
||||||
|
s.tagSettingsLock.Lock()
|
||||||
|
defer s.tagSettingsLock.Unlock()
|
||||||
|
delete(s.TagSettings, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (structField *StructField) clone() *StructField {
|
func (structField *StructField) clone() *StructField {
|
||||||
|
@ -83,6 +107,9 @@ func (structField *StructField) clone() *StructField {
|
||||||
clone.Relationship = &relationship
|
clone.Relationship = &relationship
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy the struct field tagSettings, they should be read-locked while they are copied
|
||||||
|
structField.tagSettingsLock.Lock()
|
||||||
|
defer structField.tagSettingsLock.Unlock()
|
||||||
for key, value := range structField.TagSettings {
|
for key, value := range structField.TagSettings {
|
||||||
clone.TagSettings[key] = value
|
clone.TagSettings[key] = value
|
||||||
}
|
}
|
||||||
|
@ -149,19 +176,19 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// is ignored field
|
// is ignored field
|
||||||
if _, ok := field.TagSettings["-"]; ok {
|
if _, ok := field.TagSettingsGet("-"); ok {
|
||||||
field.IsIgnored = true
|
field.IsIgnored = true
|
||||||
} else {
|
} else {
|
||||||
if _, ok := field.TagSettings["PRIMARY_KEY"]; ok {
|
if _, ok := field.TagSettingsGet("PRIMARY_KEY"); ok {
|
||||||
field.IsPrimaryKey = true
|
field.IsPrimaryKey = true
|
||||||
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
|
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := field.TagSettings["DEFAULT"]; ok {
|
if _, ok := field.TagSettingsGet("DEFAULT"); ok {
|
||||||
field.HasDefaultValue = true
|
field.HasDefaultValue = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok && !field.IsPrimaryKey {
|
if _, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok && !field.IsPrimaryKey {
|
||||||
field.HasDefaultValue = true
|
field.HasDefaultValue = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,8 +204,8 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
if indirectType.Kind() == reflect.Struct {
|
if indirectType.Kind() == reflect.Struct {
|
||||||
for i := 0; i < indirectType.NumField(); i++ {
|
for i := 0; i < indirectType.NumField(); i++ {
|
||||||
for key, value := range parseTagSetting(indirectType.Field(i).Tag) {
|
for key, value := range parseTagSetting(indirectType.Field(i).Tag) {
|
||||||
if _, ok := field.TagSettings[key]; !ok {
|
if _, ok := field.TagSettingsGet(key); !ok {
|
||||||
field.TagSettings[key] = value
|
field.TagSettingsSet(key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,17 +213,17 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
} else if _, isTime := fieldValue.(*time.Time); isTime {
|
} else if _, isTime := fieldValue.(*time.Time); isTime {
|
||||||
// is time
|
// is time
|
||||||
field.IsNormal = true
|
field.IsNormal = true
|
||||||
} else if _, ok := field.TagSettings["EMBEDDED"]; ok || fieldStruct.Anonymous {
|
} else if _, ok := field.TagSettingsGet("EMBEDDED"); ok || fieldStruct.Anonymous {
|
||||||
// is embedded struct
|
// is embedded struct
|
||||||
for _, subField := range scope.New(fieldValue).GetModelStruct().StructFields {
|
for _, subField := range scope.New(fieldValue).GetModelStruct().StructFields {
|
||||||
subField = subField.clone()
|
subField = subField.clone()
|
||||||
subField.Names = append([]string{fieldStruct.Name}, subField.Names...)
|
subField.Names = append([]string{fieldStruct.Name}, subField.Names...)
|
||||||
if prefix, ok := field.TagSettings["EMBEDDED_PREFIX"]; ok {
|
if prefix, ok := field.TagSettingsGet("EMBEDDED_PREFIX"); ok {
|
||||||
subField.DBName = prefix + subField.DBName
|
subField.DBName = prefix + subField.DBName
|
||||||
}
|
}
|
||||||
|
|
||||||
if subField.IsPrimaryKey {
|
if subField.IsPrimaryKey {
|
||||||
if _, ok := subField.TagSettings["PRIMARY_KEY"]; ok {
|
if _, ok := subField.TagSettingsGet("PRIMARY_KEY"); ok {
|
||||||
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, subField)
|
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, subField)
|
||||||
} else {
|
} else {
|
||||||
subField.IsPrimaryKey = false
|
subField.IsPrimaryKey = false
|
||||||
|
@ -227,13 +254,13 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
elemType = field.Struct.Type
|
elemType = field.Struct.Type
|
||||||
)
|
)
|
||||||
|
|
||||||
if foreignKey := field.TagSettings["FOREIGNKEY"]; foreignKey != "" {
|
if foreignKey, _ := field.TagSettingsGet("FOREIGNKEY"); foreignKey != "" {
|
||||||
foreignKeys = strings.Split(foreignKey, ",")
|
foreignKeys = strings.Split(foreignKey, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
if foreignKey := field.TagSettings["ASSOCIATION_FOREIGNKEY"]; foreignKey != "" {
|
if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_FOREIGNKEY"); foreignKey != "" {
|
||||||
associationForeignKeys = strings.Split(foreignKey, ",")
|
associationForeignKeys = strings.Split(foreignKey, ",")
|
||||||
} else if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" {
|
} else if foreignKey, _ := field.TagSettingsGet("ASSOCIATIONFOREIGNKEY"); foreignKey != "" {
|
||||||
associationForeignKeys = strings.Split(foreignKey, ",")
|
associationForeignKeys = strings.Split(foreignKey, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,13 +269,13 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
}
|
}
|
||||||
|
|
||||||
if elemType.Kind() == reflect.Struct {
|
if elemType.Kind() == reflect.Struct {
|
||||||
if many2many := field.TagSettings["MANY2MANY"]; many2many != "" {
|
if many2many, _ := field.TagSettingsGet("MANY2MANY"); many2many != "" {
|
||||||
relationship.Kind = "many_to_many"
|
relationship.Kind = "many_to_many"
|
||||||
|
|
||||||
{ // Foreign Keys for Source
|
{ // Foreign Keys for Source
|
||||||
joinTableDBNames := []string{}
|
joinTableDBNames := []string{}
|
||||||
|
|
||||||
if foreignKey := field.TagSettings["JOINTABLE_FOREIGNKEY"]; foreignKey != "" {
|
if foreignKey, _ := field.TagSettingsGet("JOINTABLE_FOREIGNKEY"); foreignKey != "" {
|
||||||
joinTableDBNames = strings.Split(foreignKey, ",")
|
joinTableDBNames = strings.Split(foreignKey, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +306,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
{ // Foreign Keys for Association (Destination)
|
{ // Foreign Keys for Association (Destination)
|
||||||
associationJoinTableDBNames := []string{}
|
associationJoinTableDBNames := []string{}
|
||||||
|
|
||||||
if foreignKey := field.TagSettings["ASSOCIATION_JOINTABLE_FOREIGNKEY"]; foreignKey != "" {
|
if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_JOINTABLE_FOREIGNKEY"); foreignKey != "" {
|
||||||
associationJoinTableDBNames = strings.Split(foreignKey, ",")
|
associationJoinTableDBNames = strings.Split(foreignKey, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,7 +344,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
var toFields = toScope.GetStructFields()
|
var toFields = toScope.GetStructFields()
|
||||||
relationship.Kind = "has_many"
|
relationship.Kind = "has_many"
|
||||||
|
|
||||||
if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {
|
if polymorphic, _ := field.TagSettingsGet("POLYMORPHIC"); polymorphic != "" {
|
||||||
// Dog has many toys, tag polymorphic is Owner, then associationType is Owner
|
// Dog has many toys, tag polymorphic is Owner, then associationType is Owner
|
||||||
// Toy use OwnerID, OwnerType ('dogs') as foreign key
|
// Toy use OwnerID, OwnerType ('dogs') as foreign key
|
||||||
if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil {
|
if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil {
|
||||||
|
@ -325,7 +352,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
relationship.PolymorphicType = polymorphicType.Name
|
relationship.PolymorphicType = polymorphicType.Name
|
||||||
relationship.PolymorphicDBName = polymorphicType.DBName
|
relationship.PolymorphicDBName = polymorphicType.DBName
|
||||||
// if Dog has multiple set of toys set name of the set (instead of default 'dogs')
|
// if Dog has multiple set of toys set name of the set (instead of default 'dogs')
|
||||||
if value, ok := field.TagSettings["POLYMORPHIC_VALUE"]; ok {
|
if value, ok := field.TagSettingsGet("POLYMORPHIC_VALUE"); ok {
|
||||||
relationship.PolymorphicValue = value
|
relationship.PolymorphicValue = value
|
||||||
} else {
|
} else {
|
||||||
relationship.PolymorphicValue = scope.TableName()
|
relationship.PolymorphicValue = scope.TableName()
|
||||||
|
@ -407,17 +434,17 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
tagAssociationForeignKeys []string
|
tagAssociationForeignKeys []string
|
||||||
)
|
)
|
||||||
|
|
||||||
if foreignKey := field.TagSettings["FOREIGNKEY"]; foreignKey != "" {
|
if foreignKey, _ := field.TagSettingsGet("FOREIGNKEY"); foreignKey != "" {
|
||||||
tagForeignKeys = strings.Split(foreignKey, ",")
|
tagForeignKeys = strings.Split(foreignKey, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
if foreignKey := field.TagSettings["ASSOCIATION_FOREIGNKEY"]; foreignKey != "" {
|
if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_FOREIGNKEY"); foreignKey != "" {
|
||||||
tagAssociationForeignKeys = strings.Split(foreignKey, ",")
|
tagAssociationForeignKeys = strings.Split(foreignKey, ",")
|
||||||
} else if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" {
|
} else if foreignKey, _ := field.TagSettingsGet("ASSOCIATIONFOREIGNKEY"); foreignKey != "" {
|
||||||
tagAssociationForeignKeys = strings.Split(foreignKey, ",")
|
tagAssociationForeignKeys = strings.Split(foreignKey, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {
|
if polymorphic, _ := field.TagSettingsGet("POLYMORPHIC"); polymorphic != "" {
|
||||||
// Cat has one toy, tag polymorphic is Owner, then associationType is Owner
|
// Cat has one toy, tag polymorphic is Owner, then associationType is Owner
|
||||||
// Toy use OwnerID, OwnerType ('cats') as foreign key
|
// Toy use OwnerID, OwnerType ('cats') as foreign key
|
||||||
if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil {
|
if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil {
|
||||||
|
@ -425,7 +452,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
relationship.PolymorphicType = polymorphicType.Name
|
relationship.PolymorphicType = polymorphicType.Name
|
||||||
relationship.PolymorphicDBName = polymorphicType.DBName
|
relationship.PolymorphicDBName = polymorphicType.DBName
|
||||||
// if Cat has several different types of toys set name for each (instead of default 'cats')
|
// if Cat has several different types of toys set name for each (instead of default 'cats')
|
||||||
if value, ok := field.TagSettings["POLYMORPHIC_VALUE"]; ok {
|
if value, ok := field.TagSettingsGet("POLYMORPHIC_VALUE"); ok {
|
||||||
relationship.PolymorphicValue = value
|
relationship.PolymorphicValue = value
|
||||||
} else {
|
} else {
|
||||||
relationship.PolymorphicValue = scope.TableName()
|
relationship.PolymorphicValue = scope.TableName()
|
||||||
|
@ -563,7 +590,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even it is ignored, also possible to decode db value into the field
|
// Even it is ignored, also possible to decode db value into the field
|
||||||
if value, ok := field.TagSettings["COLUMN"]; ok {
|
if value, ok := field.TagSettingsGet("COLUMN"); ok {
|
||||||
field.DBName = value
|
field.DBName = value
|
||||||
} else {
|
} else {
|
||||||
field.DBName = ToColumnName(fieldStruct.Name)
|
field.DBName = ToColumnName(fieldStruct.Name)
|
||||||
|
|
12
scope.go
12
scope.go
|
@ -1115,8 +1115,8 @@ func (scope *Scope) createJoinTable(field *StructField) {
|
||||||
if field, ok := scope.FieldByName(fieldName); ok {
|
if field, ok := scope.FieldByName(fieldName); ok {
|
||||||
foreignKeyStruct := field.clone()
|
foreignKeyStruct := field.clone()
|
||||||
foreignKeyStruct.IsPrimaryKey = false
|
foreignKeyStruct.IsPrimaryKey = false
|
||||||
foreignKeyStruct.TagSettings["IS_JOINTABLE_FOREIGNKEY"] = "true"
|
foreignKeyStruct.TagSettingsSet("IS_JOINTABLE_FOREIGNKEY", "true")
|
||||||
delete(foreignKeyStruct.TagSettings, "AUTO_INCREMENT")
|
foreignKeyStruct.TagSettingsDelete("AUTO_INCREMENT")
|
||||||
sqlTypes = append(sqlTypes, scope.Quote(relationship.ForeignDBNames[idx])+" "+scope.Dialect().DataTypeOf(foreignKeyStruct))
|
sqlTypes = append(sqlTypes, scope.Quote(relationship.ForeignDBNames[idx])+" "+scope.Dialect().DataTypeOf(foreignKeyStruct))
|
||||||
primaryKeys = append(primaryKeys, scope.Quote(relationship.ForeignDBNames[idx]))
|
primaryKeys = append(primaryKeys, scope.Quote(relationship.ForeignDBNames[idx]))
|
||||||
}
|
}
|
||||||
|
@ -1126,8 +1126,8 @@ func (scope *Scope) createJoinTable(field *StructField) {
|
||||||
if field, ok := toScope.FieldByName(fieldName); ok {
|
if field, ok := toScope.FieldByName(fieldName); ok {
|
||||||
foreignKeyStruct := field.clone()
|
foreignKeyStruct := field.clone()
|
||||||
foreignKeyStruct.IsPrimaryKey = false
|
foreignKeyStruct.IsPrimaryKey = false
|
||||||
foreignKeyStruct.TagSettings["IS_JOINTABLE_FOREIGNKEY"] = "true"
|
foreignKeyStruct.TagSettingsSet("IS_JOINTABLE_FOREIGNKEY", "true")
|
||||||
delete(foreignKeyStruct.TagSettings, "AUTO_INCREMENT")
|
foreignKeyStruct.TagSettingsDelete("AUTO_INCREMENT")
|
||||||
sqlTypes = append(sqlTypes, scope.Quote(relationship.AssociationForeignDBNames[idx])+" "+scope.Dialect().DataTypeOf(foreignKeyStruct))
|
sqlTypes = append(sqlTypes, scope.Quote(relationship.AssociationForeignDBNames[idx])+" "+scope.Dialect().DataTypeOf(foreignKeyStruct))
|
||||||
primaryKeys = append(primaryKeys, scope.Quote(relationship.AssociationForeignDBNames[idx]))
|
primaryKeys = append(primaryKeys, scope.Quote(relationship.AssociationForeignDBNames[idx]))
|
||||||
}
|
}
|
||||||
|
@ -1262,7 +1262,7 @@ func (scope *Scope) autoIndex() *Scope {
|
||||||
var uniqueIndexes = map[string][]string{}
|
var uniqueIndexes = map[string][]string{}
|
||||||
|
|
||||||
for _, field := range scope.GetStructFields() {
|
for _, field := range scope.GetStructFields() {
|
||||||
if name, ok := field.TagSettings["INDEX"]; ok {
|
if name, ok := field.TagSettingsGet("INDEX"); ok {
|
||||||
names := strings.Split(name, ",")
|
names := strings.Split(name, ",")
|
||||||
|
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
|
@ -1273,7 +1273,7 @@ func (scope *Scope) autoIndex() *Scope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if name, ok := field.TagSettings["UNIQUE_INDEX"]; ok {
|
if name, ok := field.TagSettingsGet("UNIQUE_INDEX"); ok {
|
||||||
names := strings.Split(name, ",")
|
names := strings.Split(name, ",")
|
||||||
|
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
|
|
Loading…
Reference in New Issue