mirror of https://github.com/go-gorm/gorm.git
Merge pull request #2396 from emirb/fix-singulartable-race-condition
Fix SingularTable race condition
This commit is contained in:
commit
e3987fd4b8
4
main.go
4
main.go
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
// DB contains information for current db connection
|
// DB contains information for current db connection
|
||||||
type DB struct {
|
type DB struct {
|
||||||
|
sync.RWMutex
|
||||||
Value interface{}
|
Value interface{}
|
||||||
Error error
|
Error error
|
||||||
RowsAffected int64
|
RowsAffected int64
|
||||||
|
@ -170,7 +171,8 @@ func (s *DB) HasBlockGlobalUpdate() bool {
|
||||||
|
|
||||||
// SingularTable use singular table by default
|
// SingularTable use singular table by default
|
||||||
func (s *DB) SingularTable(enable bool) {
|
func (s *DB) SingularTable(enable bool) {
|
||||||
modelStructsMap = sync.Map{}
|
s.parent.Lock()
|
||||||
|
defer s.parent.Unlock()
|
||||||
s.parent.singularTable = enable
|
s.parent.singularTable = enable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
33
main_test.go
33
main_test.go
|
@ -9,6 +9,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -277,6 +278,30 @@ func TestTableName(t *testing.T) {
|
||||||
DB.SingularTable(false)
|
DB.SingularTable(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTableNameConcurrently(t *testing.T) {
|
||||||
|
DB := DB.Model("")
|
||||||
|
if DB.NewScope(Order{}).TableName() != "orders" {
|
||||||
|
t.Errorf("Order's table name should be orders")
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(10)
|
||||||
|
|
||||||
|
for i := 1; i <= 10; i++ {
|
||||||
|
go func(db *gorm.DB) {
|
||||||
|
DB.SingularTable(true)
|
||||||
|
wg.Done()
|
||||||
|
}(DB)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if DB.NewScope(Order{}).TableName() != "order" {
|
||||||
|
t.Errorf("Order's singular table name should be order")
|
||||||
|
}
|
||||||
|
|
||||||
|
DB.SingularTable(false)
|
||||||
|
}
|
||||||
|
|
||||||
func TestNullValues(t *testing.T) {
|
func TestNullValues(t *testing.T) {
|
||||||
DB.DropTable(&NullValue{})
|
DB.DropTable(&NullValue{})
|
||||||
DB.AutoMigrate(&NullValue{})
|
DB.AutoMigrate(&NullValue{})
|
||||||
|
@ -1066,12 +1091,12 @@ func TestCountWithHaving(t *testing.T) {
|
||||||
|
|
||||||
DB.Create(getPreparedUser("user1", "pluck_user"))
|
DB.Create(getPreparedUser("user1", "pluck_user"))
|
||||||
DB.Create(getPreparedUser("user2", "pluck_user"))
|
DB.Create(getPreparedUser("user2", "pluck_user"))
|
||||||
user3:=getPreparedUser("user3", "pluck_user")
|
user3 := getPreparedUser("user3", "pluck_user")
|
||||||
user3.Languages=[]Language{}
|
user3.Languages = []Language{}
|
||||||
DB.Create(user3)
|
DB.Create(user3)
|
||||||
|
|
||||||
var count int
|
var count int
|
||||||
err:=db.Model(User{}).Select("users.id").
|
err := db.Model(User{}).Select("users.id").
|
||||||
Joins("LEFT JOIN user_languages ON user_languages.user_id = users.id").
|
Joins("LEFT JOIN user_languages ON user_languages.user_id = users.id").
|
||||||
Joins("LEFT JOIN languages ON user_languages.language_id = languages.id").
|
Joins("LEFT JOIN languages ON user_languages.language_id = languages.id").
|
||||||
Group("users.id").Having("COUNT(languages.id) > 1").Count(&count).Error
|
Group("users.id").Having("COUNT(languages.id) > 1").Count(&count).Error
|
||||||
|
@ -1080,7 +1105,7 @@ func TestCountWithHaving(t *testing.T) {
|
||||||
t.Error("Unexpected error on query count with having")
|
t.Error("Unexpected error on query count with having")
|
||||||
}
|
}
|
||||||
|
|
||||||
if count!=2{
|
if count != 2 {
|
||||||
t.Error("Unexpected result on query count with having")
|
t.Error("Unexpected result on query count with having")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,9 +40,11 @@ func (s *ModelStruct) TableName(db *DB) string {
|
||||||
s.defaultTableName = tabler.TableName()
|
s.defaultTableName = tabler.TableName()
|
||||||
} else {
|
} else {
|
||||||
tableName := ToTableName(s.ModelType.Name())
|
tableName := ToTableName(s.ModelType.Name())
|
||||||
|
db.parent.RLock()
|
||||||
if db == nil || (db.parent != nil && !db.parent.singularTable) {
|
if db == nil || (db.parent != nil && !db.parent.singularTable) {
|
||||||
tableName = inflection.Plural(tableName)
|
tableName = inflection.Plural(tableName)
|
||||||
}
|
}
|
||||||
|
db.parent.RUnlock()
|
||||||
s.defaultTableName = tableName
|
s.defaultTableName = tableName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,7 +165,18 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Cached model struct
|
// Get Cached model struct
|
||||||
if value, ok := modelStructsMap.Load(reflectType); ok && value != nil {
|
isSingularTable := false
|
||||||
|
if scope.db != nil && scope.db.parent != nil {
|
||||||
|
scope.db.parent.RLock()
|
||||||
|
isSingularTable = scope.db.parent.singularTable
|
||||||
|
scope.db.parent.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
hashKey := struct {
|
||||||
|
singularTable bool
|
||||||
|
reflectType reflect.Type
|
||||||
|
}{isSingularTable, reflectType}
|
||||||
|
if value, ok := modelStructsMap.Load(hashKey); ok && value != nil {
|
||||||
return value.(*ModelStruct)
|
return value.(*ModelStruct)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,7 +625,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
modelStructsMap.Store(reflectType, &modelStruct)
|
modelStructsMap.Store(hashKey, &modelStruct)
|
||||||
|
|
||||||
return &modelStruct
|
return &modelStruct
|
||||||
}
|
}
|
||||||
|
|
|
@ -1677,7 +1677,7 @@ func TestPreloadManyToManyCallbacks(t *testing.T) {
|
||||||
lvl := Level1{
|
lvl := Level1{
|
||||||
Name: "l1",
|
Name: "l1",
|
||||||
Level2s: []Level2{
|
Level2s: []Level2{
|
||||||
Level2{Name: "l2-1"}, Level2{Name: "l2-2"},
|
{Name: "l2-1"}, {Name: "l2-2"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
DB.Save(&lvl)
|
DB.Save(&lvl)
|
||||||
|
|
24
wercker.yml
24
wercker.yml
|
@ -83,7 +83,7 @@ build:
|
||||||
code: |
|
code: |
|
||||||
cd $WERCKER_SOURCE_DIR
|
cd $WERCKER_SOURCE_DIR
|
||||||
go version
|
go version
|
||||||
go get -t ./...
|
go get -t -v ./...
|
||||||
|
|
||||||
# Build the project
|
# Build the project
|
||||||
- script:
|
- script:
|
||||||
|
@ -95,54 +95,54 @@ build:
|
||||||
- script:
|
- script:
|
||||||
name: test sqlite
|
name: test sqlite
|
||||||
code: |
|
code: |
|
||||||
go test ./...
|
go test -race -v ./...
|
||||||
|
|
||||||
- script:
|
- script:
|
||||||
name: test mariadb
|
name: test mariadb
|
||||||
code: |
|
code: |
|
||||||
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mariadb:3306)/gorm?charset=utf8&parseTime=True" go test ./...
|
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mariadb:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
|
||||||
|
|
||||||
- script:
|
- script:
|
||||||
name: test mysql5.7
|
name: test mysql5.7
|
||||||
code: |
|
code: |
|
||||||
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql57:3306)/gorm?charset=utf8&parseTime=True" go test ./...
|
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql57:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
|
||||||
|
|
||||||
- script:
|
- script:
|
||||||
name: test mysql5.6
|
name: test mysql5.6
|
||||||
code: |
|
code: |
|
||||||
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql56:3306)/gorm?charset=utf8&parseTime=True" go test ./...
|
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql56:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
|
||||||
|
|
||||||
- script:
|
- script:
|
||||||
name: test mysql5.5
|
name: test mysql5.5
|
||||||
code: |
|
code: |
|
||||||
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql55:3306)/gorm?charset=utf8&parseTime=True" go test ./...
|
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql55:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
|
||||||
|
|
||||||
- script:
|
- script:
|
||||||
name: test postgres
|
name: test postgres
|
||||||
code: |
|
code: |
|
||||||
GORM_DIALECT=postgres GORM_DSN="host=postgres user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
|
GORM_DIALECT=postgres GORM_DSN="host=postgres user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test -race ./...
|
||||||
|
|
||||||
- script:
|
- script:
|
||||||
name: test postgres96
|
name: test postgres96
|
||||||
code: |
|
code: |
|
||||||
GORM_DIALECT=postgres GORM_DSN="host=postgres96 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
|
GORM_DIALECT=postgres GORM_DSN="host=postgres96 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test -race ./...
|
||||||
|
|
||||||
- script:
|
- script:
|
||||||
name: test postgres95
|
name: test postgres95
|
||||||
code: |
|
code: |
|
||||||
GORM_DIALECT=postgres GORM_DSN="host=postgres95 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
|
GORM_DIALECT=postgres GORM_DSN="host=postgres95 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test -race ./...
|
||||||
|
|
||||||
- script:
|
- script:
|
||||||
name: test postgres94
|
name: test postgres94
|
||||||
code: |
|
code: |
|
||||||
GORM_DIALECT=postgres GORM_DSN="host=postgres94 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
|
GORM_DIALECT=postgres GORM_DSN="host=postgres94 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test -race ./...
|
||||||
|
|
||||||
- script:
|
- script:
|
||||||
name: test postgres93
|
name: test postgres93
|
||||||
code: |
|
code: |
|
||||||
GORM_DIALECT=postgres GORM_DSN="host=postgres93 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
|
GORM_DIALECT=postgres GORM_DSN="host=postgres93 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test -race ./...
|
||||||
|
|
||||||
- script:
|
- script:
|
||||||
name: test mssql
|
name: test mssql
|
||||||
code: |
|
code: |
|
||||||
GORM_DIALECT=mssql GORM_DSN="sqlserver://gorm:LoremIpsum86@mssql:1433?database=gorm" go test ./...
|
GORM_DIALECT=mssql GORM_DSN="sqlserver://gorm:LoremIpsum86@mssql:1433?database=gorm" go test -race ./...
|
||||||
|
|
Loading…
Reference in New Issue