Merge c53f494ff6982962e83570a6111dc51fc3d413ff into 92e08097167b30928160f3ceb6b5497ec0a1199d
This commit is contained in:
commit
a03d7d8798
@ -1218,6 +1218,7 @@ db.Where("email = ?", "x@example.org").Attrs(User{RegisteredIp: "111.111.111.111
|
||||
* Github Pages
|
||||
* AlertColumn, DropColumn
|
||||
* R/W Splitting, Validation
|
||||
* AddForeignKey compatibility with all dialects
|
||||
|
||||
# Author
|
||||
|
||||
|
20
field.go
20
field.go
@ -54,7 +54,25 @@ func (field *Field) Set(value interface{}) error {
|
||||
|
||||
// Fields get value's fields
|
||||
func (scope *Scope) Fields() map[string]*Field {
|
||||
if scope.fields == nil {
|
||||
// Recalculate if fields is empty (nil) or number of fields is LTE 1.
|
||||
//
|
||||
// Protect the `.fields' variable state from partial-initialization timing
|
||||
// issues.
|
||||
//
|
||||
// As gorm warms state and makes `.GetStructFields' calls, they lead to a long
|
||||
// deferred function in model_struct.go. Then the deferred function calls
|
||||
// `Scope.Fields', leading to another `.GetStructFields' call.. cyclically.
|
||||
// Once information is cached the cycling ends.
|
||||
//
|
||||
// This extra rule evicts incorrect/suspiciously short fields information.
|
||||
//
|
||||
// Symptom of incorrect internal state ocurring can manifest as invalid
|
||||
// insert SQL statement errors generated during `DB.Save', e.g.:
|
||||
//
|
||||
// INSERT INTO "your_table" DEFAULT VALUES RETURNING "your_table"."id"
|
||||
//
|
||||
// This is why we refresh if nil or only a single field (usually is the ID field).
|
||||
if scope.fields == nil || len(scope.fields) <= 1 {
|
||||
fields := map[string]*Field{}
|
||||
structFields := scope.GetStructFields()
|
||||
|
||||
|
68
internal_state_test.go
Normal file
68
internal_state_test.go
Normal file
@ -0,0 +1,68 @@
|
||||
package gorm_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type (
|
||||
Organization struct {
|
||||
Id int64
|
||||
Name string `sql:"type:varchar(255);not null;"`
|
||||
}
|
||||
|
||||
App struct {
|
||||
Id int64
|
||||
Organization Organization
|
||||
OrganizationId int64 `sql:"not null;"`
|
||||
Name string `sql:"type:varchar(255);not null;"`
|
||||
}
|
||||
)
|
||||
|
||||
// TestMultipleSingularTableInvocations runs `DB.SingularTable(true)' at the
|
||||
// beginning and middle of operation. The `SingularTable' call clears out all
|
||||
// of the `gorm.modelStructs' internal package state, triggering the conditions
|
||||
// where gorm must recover, or this test will not pass.
|
||||
//
|
||||
// Also see the `Scope.Fields' function in scope.go for additional information
|
||||
// and context.
|
||||
func TestMultipleSingularTableInvocations(t *testing.T) {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
DB.SingularTable(true) // Invocation #1.
|
||||
defer DB.SingularTable(false) // Restore original plurality.
|
||||
//DB.LogMode(true)
|
||||
|
||||
entities := []interface{}{
|
||||
&Organization{},
|
||||
&App{},
|
||||
}
|
||||
for i := len(entities) - 1; i >= 0; i-- {
|
||||
if err := DB.DropTableIfExists(entities[i]).Error; err != nil {
|
||||
t.Fatalf("Problem dropping table entity=%+v: %s", entities[i], err)
|
||||
}
|
||||
}
|
||||
if err := DB.AutoMigrate(entities...).Error; err != nil {
|
||||
t.Fatalf("Auto-migrate failed: %s", err)
|
||||
}
|
||||
|
||||
createFixtures(t)
|
||||
}
|
||||
|
||||
func createFixtures(t *testing.T) {
|
||||
DB.SingularTable(true) // Invocation #2. Clobber/reset internal gorm state.
|
||||
|
||||
org := &Organization{
|
||||
Name: "Some Organization for Testing",
|
||||
}
|
||||
if err := DB.Save(org).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
app := &App{
|
||||
OrganizationId: org.Id,
|
||||
Name: "my-app-for-test-purposes",
|
||||
}
|
||||
if err := DB.Save(app).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user