Fix schema initialization paths (#3825)

* Fix schema initialization paths

The initialized channel was only closed if the schema's cacheStore did not contain the embeddedCacheKey and there were no errors parsing relations.  If the key existed or an error occurred, it would not be closed. This could leave other goroutines waiting for synchronization that will never occur.

Additionally, the other code paths that wait for initialization to complete did not return the possible error.

* Unnest common schema initialization

This makes the common code path less deeply nested and the flow control easier to follow.
This commit is contained in:
Andy Bursavich 2020-12-03 19:28:38 -08:00 committed by GitHub
parent f2321ca164
commit 61d3a4d6ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 29 additions and 28 deletions

View File

@ -92,7 +92,7 @@ func Parse(dest interface{}, cacheStore *sync.Map, namer Namer) (*Schema, error)
if v, ok := cacheStore.Load(modelType); ok { if v, ok := cacheStore.Load(modelType); ok {
s := v.(*Schema) s := v.(*Schema)
<-s.initialized <-s.initialized
return s, nil return s, s.err
} }
modelValue := reflect.New(modelType) modelValue := reflect.New(modelType)
@ -223,37 +223,38 @@ func Parse(dest interface{}, cacheStore *sync.Map, namer Namer) (*Schema, error)
} }
} }
if s, loaded := cacheStore.LoadOrStore(modelType, schema); !loaded { if v, loaded := cacheStore.LoadOrStore(modelType, schema); loaded {
if _, embedded := schema.cacheStore.Load(embeddedCacheKey); !embedded { s := v.(*Schema)
for _, field := range schema.Fields { <-s.initialized
if field.DataType == "" && (field.Creatable || field.Updatable || field.Readable) { return s, s.err
if schema.parseRelation(field); schema.err != nil { }
return schema, schema.err
}
}
fieldValue := reflect.New(field.IndirectFieldType) defer close(schema.initialized)
if fc, ok := fieldValue.Interface().(CreateClausesInterface); ok { if _, embedded := schema.cacheStore.Load(embeddedCacheKey); !embedded {
field.Schema.CreateClauses = append(field.Schema.CreateClauses, fc.CreateClauses(field)...) for _, field := range schema.Fields {
} if field.DataType == "" && (field.Creatable || field.Updatable || field.Readable) {
if schema.parseRelation(field); schema.err != nil {
if fc, ok := fieldValue.Interface().(QueryClausesInterface); ok { return schema, schema.err
field.Schema.QueryClauses = append(field.Schema.QueryClauses, fc.QueryClauses(field)...)
}
if fc, ok := fieldValue.Interface().(UpdateClausesInterface); ok {
field.Schema.UpdateClauses = append(field.Schema.UpdateClauses, fc.UpdateClauses(field)...)
}
if fc, ok := fieldValue.Interface().(DeleteClausesInterface); ok {
field.Schema.DeleteClauses = append(field.Schema.DeleteClauses, fc.DeleteClauses(field)...)
} }
} }
close(schema.initialized)
fieldValue := reflect.New(field.IndirectFieldType)
if fc, ok := fieldValue.Interface().(CreateClausesInterface); ok {
field.Schema.CreateClauses = append(field.Schema.CreateClauses, fc.CreateClauses(field)...)
}
if fc, ok := fieldValue.Interface().(QueryClausesInterface); ok {
field.Schema.QueryClauses = append(field.Schema.QueryClauses, fc.QueryClauses(field)...)
}
if fc, ok := fieldValue.Interface().(UpdateClausesInterface); ok {
field.Schema.UpdateClauses = append(field.Schema.UpdateClauses, fc.UpdateClauses(field)...)
}
if fc, ok := fieldValue.Interface().(DeleteClausesInterface); ok {
field.Schema.DeleteClauses = append(field.Schema.DeleteClauses, fc.DeleteClauses(field)...)
}
} }
} else {
<-s.(*Schema).initialized
return s.(*Schema), nil
} }
return schema, schema.err return schema, schema.err