Closed luantranminh closed 4 months ago
I have a new thought! Migration stuff should belong in the migration place. That could be the reason why GORM decided to let the Migrator create Views 🤔
db.Migrator().CreateView()
but not Model.CreateViews()
Therefore, here is another proposal bases on #38, implements gormschema.WithTrigger
method.
// gormschema/gorm.go
type (
TriggerOption struct {
Query func(*gorm.DB) *gorm.DB
Replace bool
}
)
// WithTrigger sets up a trigger with the given name and options.
func WithTrigger(name string, opt TriggerOption) Option {
return func(l *Loader) {
l.afterAutoMigrate = append(l.afterAutoMigrate, func(db *gorm.DB) error {
return db.Exec(Trigger.New(name, opt).ToSQL())
})
}
}
Actually, the user can use composite_schema
datasource from atlas to combine two schemas (one from GORM, one from their) into one big schema; then atlas can handle migration for them.
Oh, thanks. I didn't know Atlas had that. I'll update my proposal soon.
Hi @giautm, I'm about to work on this one and will stick with the proposal that defines Triggers()
for the desired entity.
I read the Trigger definition
from https://pkg.go.dev/ariga.io/atlas@v0.21.1/sql/schema#Trigger, but I think it's too verbose for atlas-provider-gorm
package and will create confusion with unused fields (Table
, View
, etc.), isn't it? Therefore, I propose a clean but basic form for a Trigger
struct that inherits from the Atlas schema trigger:
type Trigger struct {
Name string
ActionTime schema.TriggerTime // BEFORE, AFTER, or INSTEAD OF.
Event schema.TriggerEvent // INSERT, UPDATE, DELETE, etc.
For schema.TriggerFor // FOR EACH ROW or FOR EACH STATEMENT.
Body string // Trigger body only.
}
Is this a good practice to have a partial struct from an existing struct and make it more maintainable? Or should I go with a new type for all fields and maintain it inside the atlas-provider-gorm
package separately?
type TriggerTime string
const (
TriggerTimeBefore TriggerTime = "BEFORE"
TriggerTimeAfter TriggerTime = "AFTER"
TriggerTimeInsteadOf TriggerTime = "INSTEAD OF"
)
type Trigger struct {
Name string
ActionTime TriggerTime // BEFORE, AFTER, or INSTEAD OF.
Event TriggerEvent // INSERT, UPDATE, DELETE, etc.
For TriggerFor // FOR EACH ROW or FOR EACH STATEMENT.
Body string // Trigger body only.
}
Proposal solution
GORM has a feature called Hooks (application-level triggers). And now, we want to support database triggers. I'll base the implementation on the Hooks implementation to do the same things.
GORM supports 4 databases now, each has its own way to define a trigger. I'll list out the trigger documentation of each database in order of complexity, from the least trigger options to the most trigger options:
We can't have a generic solution that covers all options for triggers. Therefore, I decided to limit the scope to just cover a basic form of trigger:
WHEN
a specificOPERATION
happensDO something
.1. Setting up phase
This is the definition of a trigger. One model/table can have multiple triggers
Whenever we want to attach triggers to a model, we can embed the
Triggers
2. Migration phase
We're having a custom
migrator
to create constraints. I'll base on this and expand its functionality to have a create triggers methodFor each database, we have a corresponding trigger template
Those templates will be parsed by a method of the Trigger struct