Merge c53f494ff6982962e83570a6111dc51fc3d413ff into 92e08097167b30928160f3ceb6b5497ec0a1199d

This commit is contained in:
Jay Taylor 2015-08-19 13:55:47 +00:00
commit a03d7d8798
3 changed files with 88 additions and 1 deletions

View File

@ -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

View File

@ -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
View 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)
}
}