-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Add support for dynamic GORM valuer registration and retrieval #7655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
Dynamic runtime registration of custom GORM valuers Adds a lightweight mechanism that lets external packages register custom Key Changes• Created Affected Areas• This summary was automatically generated by @propel-code-bot |
| var dynamicRegisteredGormValuer = make(map[reflect.Type]func(context.Context, *DB, any) clause.Expr) | ||
|
|
||
| // RegisterDynamicGormValuerInit shouldn't be called outside the init function | ||
| func RegisterDynamicGormValuerInit(valueType reflect.Type, valuer func(context.Context, *DB, any) clause.Expr) { | ||
| dynamicRegisteredGormValuer[valueType] = valuer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[CriticalError]
Race condition: The global map dynamicRegisteredGormValuer is written to without synchronization. If multiple packages call RegisterDynamicGormValuerInit concurrently during init() (which can happen when Go initializes packages in parallel), this will cause a data race. While the comment says "shouldn't be called outside the init function", Go doesn't guarantee sequential execution of different packages' init() functions.
Fix: Use sync.Map or protect with sync.RWMutex:
var (
dynamicRegisteredGormValuer = make(map[reflect.Type]func(context.Context, *DB, any) clause.Expr)
dynamicValuerMutex sync.RWMutex
)
func RegisterDynamicGormValuerInit(valueType reflect.Type, valuer func(context.Context, *DB, any) clause.Expr) {
dynamicValuerMutex.Lock()
defer dynamicValuerMutex.Unlock()
dynamicRegisteredGormValuer[valueType] = valuer
}
func GetDynamicGormValuer(valueType reflect.Type) func(context.Context, *DB, any) clause.Expr {
dynamicValuerMutex.RLock()
defer dynamicValuerMutex.RUnlock()
return dynamicRegisteredGormValuer[valueType]
}Context for Agents
**Race condition**: The global map `dynamicRegisteredGormValuer` is written to without synchronization. If multiple packages call `RegisterDynamicGormValuerInit` concurrently during `init()` (which can happen when Go initializes packages in parallel), this will cause a data race. While the comment says "shouldn't be called outside the init function", Go doesn't guarantee sequential execution of different packages' `init()` functions.
**Fix**: Use `sync.Map` or protect with `sync.RWMutex`:
```go
var (
dynamicRegisteredGormValuer = make(map[reflect.Type]func(context.Context, *DB, any) clause.Expr)
dynamicValuerMutex sync.RWMutex
)
func RegisterDynamicGormValuerInit(valueType reflect.Type, valuer func(context.Context, *DB, any) clause.Expr) {
dynamicValuerMutex.Lock()
defer dynamicValuerMutex.Unlock()
dynamicRegisteredGormValuer[valueType] = valuer
}
func GetDynamicGormValuer(valueType reflect.Type) func(context.Context, *DB, any) clause.Expr {
dynamicValuerMutex.RLock()
defer dynamicValuerMutex.RUnlock()
return dynamicRegisteredGormValuer[valueType]
}
```
File: valuer.go
Line: 14|
@jinzhu Just a proposal, implementation is a simple demo... Please just ignore the AI reviews😁 |
|
I don't get what the PR for, maybe you can just use the database/sql's Valuer interface? |
@jinzhu PREPARE aa FROM 'select cast(? as time), ?';
SET @a '09:00:00';
EXECUTE aa USING @a, @a;The result is: MySQL is bugly, I found a solution: PREPARE aa FROM 'select cast(cast(? as char) as time)'I don't know why MySQL is that exactly. Just workaround it, If the gorm allowing register Valuer dynamically, we can make it like |
What did this pull request do?
Since some bugly database is popular, everyone have to workaround to it.
User Case Description
I have a library https://github.com/iseki0/goda, I don't think it's a better choice to use the
GormValuerinterface directly, it will make a strong dependency to gorm, and it's unwise since not everyone use gorm. So please give me a chance to register the handler dynamiclly, if someone need, they can register the gorm-enhance-module by the effect import statments, just timetime/tzdata.About the workaround
The MySQL has a very strange behavior:
The problem is on MySQL server, not driver. The Go MySQL driver do paratermized query like the prepare command, I refer to the https://github.com/go-sql-driver/mysql.
The solution is:
Do you have any suggestions? Maybe we should fix it in the MySQL dialect. 毕竟,又不是只有我会倒这个霉,所有传字符串的都会完蛋。