From ce4668003d81a3384264a864bb66529a7fe4b932 Mon Sep 17 00:00:00 2001 From: luantranminh Date: Tue, 28 May 2024 17:12:24 +0700 Subject: [PATCH 01/11] refactor: re-order code blocks: public first --- gormschema/gorm.go | 125 ++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 64 deletions(-) diff --git a/gormschema/gorm.go b/gormschema/gorm.go index 6cfd393..fd4d026 100644 --- a/gormschema/gorm.go +++ b/gormschema/gorm.go @@ -17,15 +17,6 @@ import ( gormig "gorm.io/gorm/migrator" ) -// New returns a new Loader. -func New(dialect string, opts ...Option) *Loader { - l := &Loader{dialect: dialect, config: &gorm.Config{}} - for _, opt := range opts { - opt(l) - } - return l -} - type ( // Loader is a Loader for gorm schema. Loader struct { @@ -35,6 +26,19 @@ type ( } // Option configures the Loader. Option func(*Loader) + // ViewOption configures a viewBuilder. + ViewOption func(*viewBuilder) + // ViewDefiner defines a view. + ViewDefiner interface { + ViewDef(dialect string) []ViewOption + } + viewBuilder struct { + db *gorm.DB + createStmt string + // viewName is only used for the BuildStmt option. + // BuildStmt returns only a subquery; viewName helps to create a full CREATE VIEW statement. + viewName string + } ) // WithConfig sets the gorm config. @@ -44,6 +48,47 @@ func WithConfig(cfg *gorm.Config) Option { } } +// WithJoinTable sets up a join table for the given model and field. +// Deprecated: put the join tables alongside the models in the Load call. +func WithJoinTable(model any, field string, jointable any) Option { + return func(l *Loader) { + l.beforeAutoMigrate = append(l.beforeAutoMigrate, func(db *gorm.DB) error { + return db.SetupJoinTable(model, field, jointable) + }) + } +} + +// New returns a new Loader. +func New(dialect string, opts ...Option) *Loader { + l := &Loader{dialect: dialect, config: &gorm.Config{}} + for _, opt := range opts { + opt(l) + } + return l +} + +// CreateStmt accepts raw SQL to create a CREATE VIEW statement. +func CreateStmt(stmt string) ViewOption { + return func(b *viewBuilder) { + b.createStmt = b.db.ToSQL(func(tx *gorm.DB) *gorm.DB { + return tx.Exec(stmt) + }) + } +} + +// BuildStmt accepts a function with gorm query builder to create a CREATE VIEW statement. +// With this option, the view's name will be the same as the model's table name +func BuildStmt(fn func(db *gorm.DB) *gorm.DB) ViewOption { + return func(b *viewBuilder) { + vd := b.db.ToSQL(func(tx *gorm.DB) *gorm.DB { + return fn(tx). + Unscoped(). // Skip gorm deleted_at filtering. + Find(nil) // Execute the query and convert it to SQL. + }) + b.createStmt = fmt.Sprintf("CREATE VIEW %s AS %s", b.viewName, vd) + } +} + // Load loads the models and returns the DDL statements representing the schema. func (l *Loader) Load(models ...any) (string, error) { var ( @@ -256,61 +301,6 @@ func (m *migrator) CreateViews(views []ViewDefiner) error { return nil } -// WithJoinTable sets up a join table for the given model and field. -// Deprecated: put the join tables alongside the models in the Load call. -func WithJoinTable(model any, field string, jointable any) Option { - return func(l *Loader) { - l.beforeAutoMigrate = append(l.beforeAutoMigrate, func(db *gorm.DB) error { - return db.SetupJoinTable(model, field, jointable) - }) - } -} - -func indirect(t reflect.Type) reflect.Type { - for t.Kind() == reflect.Ptr { - t = t.Elem() - } - return t -} - -type ( - // ViewOption configures a viewBuilder. - ViewOption func(*viewBuilder) - // ViewDefiner defines a view. - ViewDefiner interface { - ViewDef(dialect string) []ViewOption - } - viewBuilder struct { - db *gorm.DB - createStmt string - // viewName is only used for the BuildStmt option. - // BuildStmt returns only a subquery; viewName helps to create a full CREATE VIEW statement. - viewName string - } -) - -// CreateStmt accepts raw SQL to create a CREATE VIEW statement. -func CreateStmt(stmt string) ViewOption { - return func(b *viewBuilder) { - b.createStmt = b.db.ToSQL(func(tx *gorm.DB) *gorm.DB { - return tx.Exec(stmt) - }) - } -} - -// BuildStmt accepts a function with gorm query builder to create a CREATE VIEW statement. -// With this option, the view's name will be the same as the model's table name -func BuildStmt(fn func(db *gorm.DB) *gorm.DB) ViewOption { - return func(b *viewBuilder) { - vd := b.db.ToSQL(func(tx *gorm.DB) *gorm.DB { - return fn(tx). - Unscoped(). // Skip gorm deleted_at filtering. - Find(nil) // Execute the query and convert it to SQL. - }) - b.createStmt = fmt.Sprintf("CREATE VIEW %s AS %s", b.viewName, vd) - } -} - // orderModels places join tables at the end of the list of models (if any), // which helps GORM resolve m2m relationships correctly. func (m *migrator) orderModels(models ...any) ([]any, error) { @@ -348,3 +338,10 @@ func (m *migrator) orderModels(models ...any) ([]any, error) { } return append(otherTables, joinTables...), nil } + +func indirect(t reflect.Type) reflect.Type { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t +} From 02989eb1941e28a49f5a003be1c73eea3b1390bf Mon Sep 17 00:00:00 2001 From: luantranminh Date: Tue, 28 May 2024 18:36:22 +0700 Subject: [PATCH 02/11] feat: allow defining Triggers() for a model --- gormschema/gorm.go | 71 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/gormschema/gorm.go b/gormschema/gorm.go index fd4d026..858818d 100644 --- a/gormschema/gorm.go +++ b/gormschema/gorm.go @@ -26,13 +26,23 @@ type ( } // Option configures the Loader. Option func(*Loader) - // ViewOption configures a viewBuilder. - ViewOption func(*viewBuilder) + // ViewOption implemented by VIEW's related options + ViewOption interface { + isViewOption() + apply(*schemaBuilder) + } + // TriggerOption implemented by TRIGGER's related options + TriggerOption interface { + isTriggerOption() + apply(*schemaBuilder) + } // ViewDefiner defines a view. ViewDefiner interface { ViewDef(dialect string) []ViewOption } - viewBuilder struct { + // schemaOption configures the schemaBuilder. + schemaOption func(*schemaBuilder) + schemaBuilder struct { db *gorm.DB createStmt string // viewName is only used for the BuildStmt option. @@ -67,26 +77,34 @@ func New(dialect string, opts ...Option) *Loader { return l } -// CreateStmt accepts raw SQL to create a CREATE VIEW statement. -func CreateStmt(stmt string) ViewOption { - return func(b *viewBuilder) { - b.createStmt = b.db.ToSQL(func(tx *gorm.DB) *gorm.DB { - return tx.Exec(stmt) - }) - } +func (s schemaOption) apply(b *schemaBuilder) { + s(b) +} + +func (schemaOption) isViewOption() {} +func (schemaOption) isTriggerOption() {} + +// CreateStmt accepts raw SQL to create a view or trigger +func CreateStmt(stmt string) interface { + ViewOption + TriggerOption +} { + return schemaOption(func(b *schemaBuilder) { + b.createStmt = stmt + }) } // BuildStmt accepts a function with gorm query builder to create a CREATE VIEW statement. // With this option, the view's name will be the same as the model's table name func BuildStmt(fn func(db *gorm.DB) *gorm.DB) ViewOption { - return func(b *viewBuilder) { + return schemaOption(func(b *schemaBuilder) { vd := b.db.ToSQL(func(tx *gorm.DB) *gorm.DB { return fn(tx). Unscoped(). // Skip gorm deleted_at filtering. Find(nil) // Execute the query and convert it to SQL. }) b.createStmt = fmt.Sprintf("CREATE VIEW %s AS %s", b.viewName, vd) - } + }) } // Load loads the models and returns the DDL statements representing the schema. @@ -170,6 +188,9 @@ func (l *Loader) Load(models ...any) (string, error) { if err = cm.CreateViews(views); err != nil { return "", err } + if err = cm.CreateTriggers(models); err != nil { + return "", err + } if !l.config.DisableForeignKeyConstraintWhenMigrating && l.dialect != "sqlite" { if err = cm.CreateConstraints(tables); err != nil { return "", err @@ -287,14 +308,14 @@ func (m *migrator) CreateViews(views []ViewDefiner) error { }); ok { viewName = namer.TableName() } - viewBuilder := &viewBuilder{ + schemaBuilder := &schemaBuilder{ db: m.DB, viewName: viewName, } for _, opt := range view.ViewDef(m.Dialector.Name()) { - opt(viewBuilder) + opt.apply(schemaBuilder) } - if err := m.DB.Exec(viewBuilder.createStmt).Error; err != nil { + if err := m.DB.Exec(schemaBuilder.createStmt).Error; err != nil { return err } } @@ -339,6 +360,26 @@ func (m *migrator) orderModels(models ...any) ([]any, error) { return append(otherTables, joinTables...), nil } +// CreateTriggers creates the triggers for the given models. +func (m *migrator) CreateTriggers(models []any) error { + for _, model := range models { + if md, ok := model.(interface { + Triggers(string) []TriggerOption + }); ok { + for _, option := range md.Triggers(m.Dialector.Name()) { + schemaBuilder := &schemaBuilder{ + db: m.DB, + } + option.apply(schemaBuilder) + if err := m.DB.Exec(schemaBuilder.createStmt).Error; err != nil { + return err + } + } + } + } + return nil +} + func indirect(t reflect.Type) reflect.Type { for t.Kind() == reflect.Ptr { t = t.Elem() From 90d1e6920ac7db1ea6240728047cd14c4be0a56a Mon Sep 17 00:00:00 2001 From: Luan Tran Date: Tue, 28 May 2024 23:35:51 +0700 Subject: [PATCH 03/11] fix: use Triggers return type as an array of Options --- gormschema/gorm.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/gormschema/gorm.go b/gormschema/gorm.go index 858818d..d8fb50a 100644 --- a/gormschema/gorm.go +++ b/gormschema/gorm.go @@ -364,15 +364,17 @@ func (m *migrator) orderModels(models ...any) ([]any, error) { func (m *migrator) CreateTriggers(models []any) error { for _, model := range models { if md, ok := model.(interface { - Triggers(string) []TriggerOption + Triggers(string) [][]TriggerOption }); ok { - for _, option := range md.Triggers(m.Dialector.Name()) { + for _, options := range md.Triggers(m.Dialector.Name()) { schemaBuilder := &schemaBuilder{ db: m.DB, } - option.apply(schemaBuilder) - if err := m.DB.Exec(schemaBuilder.createStmt).Error; err != nil { - return err + for _, option := range options { + option.apply(schemaBuilder) + if err := m.DB.Exec(schemaBuilder.createStmt).Error; err != nil { + return err + } } } } From 5ecbd77f6f87ef22130ef39aac8121dec30f4820 Mon Sep 17 00:00:00 2001 From: Luan Tran Date: Tue, 28 May 2024 23:36:51 +0700 Subject: [PATCH 04/11] tests: update test cases for trigger --- gormschema/gorm_test.go | 42 +++++++- gormschema/testdata/mysql_default | 14 +++ gormschema/testdata/postgresql_default | 26 +++++ gormschema/testdata/sqlite_default | 12 +++ gormschema/testdata/sqlite_no_fk | 14 ++- gormschema/testdata/sqlserver_default | 28 ++++++ .../migrations/mysql/20240528162003.sql | 16 +++ internal/testdata/migrations/mysql/atlas.sum | 4 +- .../migrations/postgres/20240528162135.sql | 26 +++++ .../testdata/migrations/postgres/atlas.sum | 4 +- .../migrations/sqlite/20240528162330.sql | 15 +++ internal/testdata/migrations/sqlite/atlas.sum | 4 +- .../migrations/sqlserver/20240528162241.sql | 28 ++++++ .../testdata/migrations/sqlserver/atlas.sum | 4 +- internal/testdata/models/pet.go | 99 +++++++++++++++++++ 15 files changed, 321 insertions(+), 15 deletions(-) create mode 100644 internal/testdata/migrations/mysql/20240528162003.sql create mode 100644 internal/testdata/migrations/postgres/20240528162135.sql create mode 100644 internal/testdata/migrations/sqlite/20240528162330.sql create mode 100644 internal/testdata/migrations/sqlserver/20240528162241.sql diff --git a/gormschema/gorm_test.go b/gormschema/gorm_test.go index f2d6e36..8dfe8b8 100644 --- a/gormschema/gorm_test.go +++ b/gormschema/gorm_test.go @@ -16,14 +16,22 @@ import ( func TestSQLiteConfig(t *testing.T) { resetSession() l := gormschema.New("sqlite") - sql, err := l.Load(models.WorkingAgedUsers{}, models.Pet{}, ckmodels.Event{}, ckmodels.Location{}, models.TopPetOwner{}) + sql, err := l.Load( + models.WorkingAgedUsers{}, + models.Pet{}, + models.UserPetHistory{}, + models.User{}, + ckmodels.Event{}, + ckmodels.Location{}, + models.TopPetOwner{}, + ) require.NoError(t, err) requireEqualContent(t, sql, "testdata/sqlite_default") resetSession() l = gormschema.New("sqlite", gormschema.WithConfig(&gorm.Config{ DisableForeignKeyConstraintWhenMigrating: true, })) - sql, err = l.Load(models.Pet{}, models.User{}) + sql, err = l.Load(models.UserPetHistory{}, models.Pet{}, models.User{}) require.NoError(t, err) requireEqualContent(t, sql, "testdata/sqlite_no_fk") resetSession() @@ -32,7 +40,15 @@ func TestSQLiteConfig(t *testing.T) { func TestPostgreSQLConfig(t *testing.T) { resetSession() l := gormschema.New("postgres") - sql, err := l.Load(models.WorkingAgedUsers{}, ckmodels.Location{}, ckmodels.Event{}, models.User{}, models.Pet{}, models.TopPetOwner{}) + sql, err := l.Load( + models.WorkingAgedUsers{}, + ckmodels.Location{}, + ckmodels.Event{}, + models.UserPetHistory{}, + models.User{}, + models.Pet{}, + models.TopPetOwner{}, + ) require.NoError(t, err) requireEqualContent(t, sql, "testdata/postgresql_default") resetSession() @@ -48,7 +64,15 @@ func TestPostgreSQLConfig(t *testing.T) { func TestMySQLConfig(t *testing.T) { resetSession() l := gormschema.New("mysql") - sql, err := l.Load(models.WorkingAgedUsers{}, ckmodels.Location{}, ckmodels.Event{}, models.User{}, models.Pet{}, models.TopPetOwner{}) + sql, err := l.Load( + models.WorkingAgedUsers{}, + ckmodels.Location{}, + ckmodels.Event{}, + models.UserPetHistory{}, + models.User{}, + models.Pet{}, + models.TopPetOwner{}, + ) require.NoError(t, err) requireEqualContent(t, sql, "testdata/mysql_default") resetSession() @@ -80,7 +104,15 @@ func TestMySQLConfig(t *testing.T) { func TestSQLServerConfig(t *testing.T) { resetSession() l := gormschema.New("sqlserver") - sql, err := l.Load(models.WorkingAgedUsers{}, ckmodels.Location{}, ckmodels.Event{}, models.User{}, models.Pet{}, models.TopPetOwner{}) + sql, err := l.Load( + models.WorkingAgedUsers{}, + ckmodels.Location{}, + ckmodels.Event{}, + models.UserPetHistory{}, + models.User{}, + models.Pet{}, + models.TopPetOwner{}, + ) require.NoError(t, err) requireEqualContent(t, sql, "testdata/sqlserver_default") resetSession() diff --git a/gormschema/testdata/mysql_default b/gormschema/testdata/mysql_default index 2e42336..dad5674 100644 --- a/gormschema/testdata/mysql_default +++ b/gormschema/testdata/mysql_default @@ -1,11 +1,25 @@ CREATE TABLE `events` (`eventId` varchar(191),`locationId` varchar(191),PRIMARY KEY (`eventId`),UNIQUE INDEX `idx_events_location_id` (`locationId`)); CREATE TABLE `locations` (`locationId` varchar(191),`eventId` varchar(191),PRIMARY KEY (`locationId`),UNIQUE INDEX `idx_locations_event_id` (`eventId`)); +CREATE TABLE `user_pet_histories` (`user_id` bigint unsigned,`pet_id` bigint unsigned,`created_at` datetime(3) NULL,PRIMARY KEY (`user_id`,`pet_id`)); CREATE TABLE `users` (`id` bigint unsigned AUTO_INCREMENT,`created_at` datetime(3) NULL,`updated_at` datetime(3) NULL,`deleted_at` datetime(3) NULL,`name` longtext,`age` bigint,PRIMARY KEY (`id`),INDEX `idx_users_deleted_at` (`deleted_at`)); CREATE TABLE `hobbies` (`id` bigint unsigned AUTO_INCREMENT,`name` longtext,PRIMARY KEY (`id`)); CREATE TABLE `user_hobbies` (`hobby_id` bigint unsigned,`user_id` bigint unsigned,PRIMARY KEY (`hobby_id`,`user_id`)); CREATE TABLE `pets` (`id` bigint unsigned AUTO_INCREMENT,`created_at` datetime(3) NULL,`updated_at` datetime(3) NULL,`deleted_at` datetime(3) NULL,`name` longtext,`user_id` bigint unsigned,PRIMARY KEY (`id`),INDEX `idx_pets_deleted_at` (`deleted_at`)); CREATE VIEW working_aged_users AS SELECT name, age FROM `users` WHERE age BETWEEN 18 AND 65; CREATE VIEW top_pet_owners AS SELECT user_id, COUNT(id) AS pet_count FROM pets GROUP BY user_id ORDER BY pet_count DESC LIMIT 10; +CREATE TRIGGER trg_insert_user_pet_history +AFTER INSERT ON pets +FOR EACH ROW +BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + VALUES (NEW.user_id, NEW.id, NOW(3)); +END; +CREATE TRIGGER trg_adding_heart_on_pet +BEFORE INSERT ON pets +FOR EACH ROW +BEGIN + SET NEW.name = CONCAT(NEW.name, ' ❤️'); +END; ALTER TABLE `events` ADD CONSTRAINT `fk_locations_event` FOREIGN KEY (`locationId`) REFERENCES `locations`(`locationId`); ALTER TABLE `locations` ADD CONSTRAINT `fk_events_location` FOREIGN KEY (`eventId`) REFERENCES `events`(`eventId`); ALTER TABLE `user_hobbies` ADD CONSTRAINT `fk_user_hobbies_hobby` FOREIGN KEY (`hobby_id`) REFERENCES `hobbies`(`id`); diff --git a/gormschema/testdata/postgresql_default b/gormschema/testdata/postgresql_default index d0c022d..79fc6f0 100644 --- a/gormschema/testdata/postgresql_default +++ b/gormschema/testdata/postgresql_default @@ -2,6 +2,7 @@ CREATE TABLE "events" ("eventId" varchar(191),"locationId" varchar(191),PRIMARY CREATE UNIQUE INDEX IF NOT EXISTS "idx_events_location_id" ON "events" ("locationId"); CREATE TABLE "locations" ("locationId" varchar(191),"eventId" varchar(191),PRIMARY KEY ("locationId")); CREATE UNIQUE INDEX IF NOT EXISTS "idx_locations_event_id" ON "locations" ("eventId"); +CREATE TABLE "user_pet_histories" ("user_id" bigint,"pet_id" bigint,"created_at" timestamptz,PRIMARY KEY ("user_id","pet_id")); CREATE TABLE "users" ("id" bigserial,"created_at" timestamptz,"updated_at" timestamptz,"deleted_at" timestamptz,"name" text,"age" bigint,PRIMARY KEY ("id")); CREATE INDEX IF NOT EXISTS "idx_users_deleted_at" ON "users" ("deleted_at"); CREATE TABLE "hobbies" ("id" bigserial,"name" text,PRIMARY KEY ("id")); @@ -10,6 +11,31 @@ CREATE TABLE "pets" ("id" bigserial,"created_at" timestamptz,"updated_at" timest CREATE INDEX IF NOT EXISTS "idx_pets_deleted_at" ON "pets" ("deleted_at"); CREATE VIEW working_aged_users AS SELECT name, age FROM "users" WHERE age BETWEEN 18 AND 65; CREATE VIEW top_pet_owners AS SELECT user_id, COUNT(id) AS pet_count FROM pets GROUP BY user_id ORDER BY pet_count DESC LIMIT 10; +CREATE OR REPLACE FUNCTION log_user_pet_histories() +RETURNS TRIGGER AS $$ +BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + VALUES (NEW.user_id, NEW.id, NEW.created_at); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trg_insert_user_pet_history +AFTER INSERT ON pets +FOR EACH ROW +EXECUTE FUNCTION log_user_pet_histories();; +CREATE OR REPLACE FUNCTION add_heart_on_pet() +RETURNS TRIGGER AS $$ +BEGIN + NEW.name := NEW.name || ' ❤️'; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trg_adding_heart_on_pet +BEFORE INSERT ON pets +FOR EACH ROW +EXECUTE FUNCTION add_heart_on_pet();; ALTER TABLE "events" ADD CONSTRAINT "fk_locations_event" FOREIGN KEY ("locationId") REFERENCES "locations"("locationId"); ALTER TABLE "locations" ADD CONSTRAINT "fk_events_location" FOREIGN KEY ("eventId") REFERENCES "events"("eventId"); ALTER TABLE "user_hobbies" ADD CONSTRAINT "fk_user_hobbies_hobby" FOREIGN KEY ("hobby_id") REFERENCES "hobbies"("id"); diff --git a/gormschema/testdata/sqlite_default b/gormschema/testdata/sqlite_default index ba84c95..2a035b1 100644 --- a/gormschema/testdata/sqlite_default +++ b/gormschema/testdata/sqlite_default @@ -2,9 +2,21 @@ CREATE TABLE `users` (`id` integer,`created_at` datetime,`updated_at` datetime,` CREATE INDEX `idx_users_deleted_at` ON `users`(`deleted_at`); CREATE TABLE `pets` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`name` text,`user_id` integer,PRIMARY KEY (`id`),CONSTRAINT `fk_users_pets` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)); CREATE INDEX `idx_pets_deleted_at` ON `pets`(`deleted_at`); +CREATE TABLE `user_pet_histories` (`user_id` integer,`pet_id` integer,`created_at` datetime,PRIMARY KEY (`user_id`,`pet_id`)); CREATE TABLE `locations` (`locationId` text,`eventId` text,PRIMARY KEY (`locationId`),CONSTRAINT `fk_events_location` FOREIGN KEY (`eventId`) REFERENCES `events`(`eventId`)); CREATE UNIQUE INDEX `idx_locations_event_id` ON `locations`(`eventId`); CREATE TABLE `events` (`eventId` text,`locationId` text,PRIMARY KEY (`eventId`),CONSTRAINT `fk_locations_event` FOREIGN KEY (`locationId`) REFERENCES `locations`(`locationId`)); CREATE UNIQUE INDEX `idx_events_location_id` ON `events`(`locationId`); CREATE VIEW working_aged_users AS SELECT name, age FROM `users` WHERE age BETWEEN 18 AND 65; CREATE VIEW top_pet_owners AS SELECT user_id, COUNT(id) AS pet_count FROM pets GROUP BY user_id ORDER BY pet_count DESC LIMIT 10; +CREATE TRIGGER trg_insert_user_pet_history +AFTER INSERT ON pets +BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + VALUES (NEW.user_id, NEW.id, datetime('now')); +END; +CREATE TRIGGER trg_adding_heart_on_pet +BEFORE INSERT ON pets +BEGIN + UPDATE pets SET name = name || ' ❤️' WHERE id = NEW.id; +END; diff --git a/gormschema/testdata/sqlite_no_fk b/gormschema/testdata/sqlite_no_fk index 0405a77..eb94019 100644 --- a/gormschema/testdata/sqlite_no_fk +++ b/gormschema/testdata/sqlite_no_fk @@ -1,6 +1,16 @@ +CREATE TABLE `user_pet_histories` (`user_id` integer,`pet_id` integer,`created_at` datetime,PRIMARY KEY (`user_id`,`pet_id`)); CREATE TABLE `users` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`name` text,`age` integer,PRIMARY KEY (`id`)); CREATE INDEX `idx_users_deleted_at` ON `users`(`deleted_at`); CREATE TABLE `pets` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`name` text,`user_id` integer,PRIMARY KEY (`id`)); CREATE INDEX `idx_pets_deleted_at` ON `pets`(`deleted_at`); -CREATE TABLE `hobbies` (`id` integer,`name` text,PRIMARY KEY (`id`)); -CREATE TABLE `user_hobbies` (`hobby_id` integer,`user_id` integer,PRIMARY KEY (`hobby_id`,`user_id`)); +CREATE TRIGGER trg_insert_user_pet_history +AFTER INSERT ON pets +BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + VALUES (NEW.user_id, NEW.id, datetime('now')); +END; +CREATE TRIGGER trg_adding_heart_on_pet +BEFORE INSERT ON pets +BEGIN + UPDATE pets SET name = name || ' ❤️' WHERE id = NEW.id; +END; diff --git a/gormschema/testdata/sqlserver_default b/gormschema/testdata/sqlserver_default index 1f4c36e..7f19601 100644 --- a/gormschema/testdata/sqlserver_default +++ b/gormschema/testdata/sqlserver_default @@ -2,6 +2,7 @@ CREATE TABLE "events" ("eventId" nvarchar(191),"locationId" nvarchar(191),PRIMAR CREATE UNIQUE INDEX "idx_events_location_id" ON "events"("locationId"); CREATE TABLE "locations" ("locationId" nvarchar(191),"eventId" nvarchar(191),PRIMARY KEY ("locationId")); CREATE UNIQUE INDEX "idx_locations_event_id" ON "locations"("eventId"); +CREATE TABLE "user_pet_histories" ("user_id" bigint,"pet_id" bigint,"created_at" datetimeoffset,PRIMARY KEY ("user_id","pet_id")); CREATE TABLE "users" ("id" bigint IDENTITY(1,1),"created_at" datetimeoffset,"updated_at" datetimeoffset,"deleted_at" datetimeoffset,"name" nvarchar(MAX),"age" bigint,PRIMARY KEY ("id")); CREATE INDEX "idx_users_deleted_at" ON "users"("deleted_at"); CREATE TABLE "hobbies" ("id" bigint IDENTITY(1,1),"name" nvarchar(MAX),PRIMARY KEY ("id")); @@ -10,6 +11,33 @@ CREATE TABLE "pets" ("id" bigint IDENTITY(1,1),"created_at" datetimeoffset,"upda CREATE INDEX "idx_pets_deleted_at" ON "pets"("deleted_at"); CREATE VIEW working_aged_users AS SELECT name, age FROM "users" WHERE age BETWEEN 18 AND 65; CREATE VIEW top_pet_owners AS SELECT user_id, COUNT(id) AS pet_count FROM pets GROUP BY user_id ORDER BY pet_count DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY; +CREATE TRIGGER trg_insert_user_pet_history +ON pets +AFTER INSERT +AS +BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + SELECT + inserted.user_id, + inserted.id, + GETDATE() + FROM + inserted + WHERE + inserted.user_id IS NOT NULL; +END; +CREATE TRIGGER trg_adding_heart_on_pet +ON pets +INSTEAD OF INSERT +AS +BEGIN + INSERT INTO pets (name, user_id) + SELECT + CONCAT(inserted.name, ' ❤️'), + inserted.user_id + FROM + inserted; +END; ALTER TABLE "events" ADD CONSTRAINT "fk_locations_event" FOREIGN KEY ("locationId") REFERENCES "locations"("locationId"); ALTER TABLE "locations" ADD CONSTRAINT "fk_events_location" FOREIGN KEY ("eventId") REFERENCES "events"("eventId"); ALTER TABLE "user_hobbies" ADD CONSTRAINT "fk_user_hobbies_hobby" FOREIGN KEY ("hobby_id") REFERENCES "hobbies"("id"); diff --git a/internal/testdata/migrations/mysql/20240528162003.sql b/internal/testdata/migrations/mysql/20240528162003.sql new file mode 100644 index 0000000..f87479c --- /dev/null +++ b/internal/testdata/migrations/mysql/20240528162003.sql @@ -0,0 +1,16 @@ +-- Create trigger "trg_adding_heart_on_pet" +CREATE TRIGGER `trg_adding_heart_on_pet` BEFORE INSERT ON `pets` FOR EACH ROW BEGIN + SET NEW.name = CONCAT(NEW.name, ' ❤️'); +END; +-- Create trigger "trg_insert_user_pet_history" +CREATE TRIGGER `trg_insert_user_pet_history` AFTER INSERT ON `pets` FOR EACH ROW BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + VALUES (NEW.user_id, NEW.id, NOW(3)); +END; +-- Create "user_pet_histories" table +CREATE TABLE `user_pet_histories` ( + `user_id` bigint unsigned NOT NULL, + `pet_id` bigint unsigned NOT NULL, + `created_at` datetime(3) NULL, + PRIMARY KEY (`user_id`, `pet_id`) +) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci; diff --git a/internal/testdata/migrations/mysql/atlas.sum b/internal/testdata/migrations/mysql/atlas.sum index 925ebbd..f6a1bc8 100644 --- a/internal/testdata/migrations/mysql/atlas.sum +++ b/internal/testdata/migrations/mysql/atlas.sum @@ -1,5 +1,5 @@ -h1:4neLwABljo1WE/2TflQvcvZCa4xR025viAzNEa4Cp6s= +h1:k7/CcbTblLR2glTHovuXUh3GeYYyJOPF92p9JTARekQ= 20230627123246.sql h1:+bgzC3WJyyIR6Rv/FUvaNXJ1gkbKJlYcEMgp69yORIY= 20240512024238.sql h1:2kQL4tE/tAhvXuozmRAJ3oXTo1KRz11QosVDw+0va14= 20240518091603.sql h1:xNClqqRaOjXwg0julpsiPYsmqUcEL/hJel1iqYzi3DM= -20240601122756.sql h1:5+N9UzKTw6qQvJLf8nGw4BdFuHG0fa2JHdHn++G8MuE= +20240528162003.sql h1:uNDd913fXzlpf8xgLiLsfCaoeZsbQkGPfZ5DXQOXVaU= diff --git a/internal/testdata/migrations/postgres/20240528162135.sql b/internal/testdata/migrations/postgres/20240528162135.sql new file mode 100644 index 0000000..d356908 --- /dev/null +++ b/internal/testdata/migrations/postgres/20240528162135.sql @@ -0,0 +1,26 @@ +-- Create "log_user_pet_histories" function +CREATE FUNCTION "public"."log_user_pet_histories" () RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + VALUES (NEW.user_id, NEW.id, NEW.created_at); + RETURN NEW; +END; +$$; +-- Create trigger "trg_insert_user_pet_history" +CREATE TRIGGER "trg_insert_user_pet_history" AFTER INSERT ON "public"."pets" FOR EACH ROW EXECUTE FUNCTION "public"."log_user_pet_histories"(); +-- Create "add_heart_on_pet" function +CREATE FUNCTION "public"."add_heart_on_pet" () RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + NEW.name := NEW.name || ' ❤️'; + RETURN NEW; +END; +$$; +-- Create trigger "trg_adding_heart_on_pet" +CREATE TRIGGER "trg_adding_heart_on_pet" BEFORE INSERT ON "public"."pets" FOR EACH ROW EXECUTE FUNCTION "public"."add_heart_on_pet"(); +-- Create "user_pet_histories" table +CREATE TABLE "public"."user_pet_histories" ( + "user_id" bigint NOT NULL, + "pet_id" bigint NOT NULL, + "created_at" timestamptz NULL, + PRIMARY KEY ("user_id", "pet_id") +); diff --git a/internal/testdata/migrations/postgres/atlas.sum b/internal/testdata/migrations/postgres/atlas.sum index fa401b8..778890e 100644 --- a/internal/testdata/migrations/postgres/atlas.sum +++ b/internal/testdata/migrations/postgres/atlas.sum @@ -1,5 +1,5 @@ -h1:a/bAYykD/mWdu/8WM16vbYqnwg5NsdX2iM3urGrBFUc= +h1:DmCb55HYPLacSRaYyN6i+dbYVjh2so0p/xGJgkQY4Nw= 20230627123049.sql h1:1jYJM2+VCr9152vg6gayCrcEvuT/FE7ufOyZ86VLaOE= 20240512024223.sql h1:RY4w148OJBBr5sXpfBq6B48p/1cFrTEpH4TlwD7mUps= 20240518091611.sql h1:3Kv6mYS8ML72H6HE5qr/a2gdVUrfWuHVufP/1wl70vE= -20240601122813.sql h1:ALbhVYVMyOvZSy5jmBVrsk4xjdkx6ro8OxKJcmW0n1U= +20240528162135.sql h1:3KF6tW9nyVF010sKTm3pRN9rtuXTTjOx0uG+SMaVim0= diff --git a/internal/testdata/migrations/sqlite/20240528162330.sql b/internal/testdata/migrations/sqlite/20240528162330.sql new file mode 100644 index 0000000..e902905 --- /dev/null +++ b/internal/testdata/migrations/sqlite/20240528162330.sql @@ -0,0 +1,15 @@ +-- Create trigger "trg_insert_user_pet_history" +CREATE TRIGGER `trg_insert_user_pet_history` AFTER INSERT ON `pets` FOR EACH ROW BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + VALUES (NEW.user_id, NEW.id, datetime('now')); +END; +-- Create trigger "trg_adding_heart_on_pet" +CREATE TRIGGER `trg_adding_heart_on_pet` BEFORE INSERT ON `pets` FOR EACH ROW BEGIN + UPDATE pets SET name = name || ' ❤️' WHERE id = NEW.id; +-- Create "user_pet_histories" table +CREATE TABLE `user_pet_histories` ( + `user_id` integer NULL, + `pet_id` integer NULL, + `created_at` datetime NULL, + PRIMARY KEY (`user_id`, `pet_id`) +); diff --git a/internal/testdata/migrations/sqlite/atlas.sum b/internal/testdata/migrations/sqlite/atlas.sum index 4bbb8ac..ab530b8 100644 --- a/internal/testdata/migrations/sqlite/atlas.sum +++ b/internal/testdata/migrations/sqlite/atlas.sum @@ -1,5 +1,5 @@ -h1:n3NYZrFWODZNTYXEomQ/EtMuxc7EsQDZ7jaIgd9olYY= +h1:9ZIWiyIkB+4j2ihwBIL60jZq7j4EPesQQv04/sb4SQc= 20230627123228.sql h1:YfwJdN73sWz1G5/0tU2BtGLyzJCfRQr8blTSquUZ+qo= 20240511123637.sql h1:Kbk3wUzTfBbq8mDdTT08hP93ecNU0y5oTL+O8idEcdQ= 20240518091437.sql h1:svMANRZuZDvgzqO3iyNLUjrUrK8BTMEB2f0NV3d5sJo= -20240601122746.sql h1:Jpzp4UdG5EjO8uNtRWU8sZ9fKuP4mRod5ofyFVQwH1A= +20240528162330.sql h1:G8xUMjRgqGYYuDElT6PdNj/0k2HR+xYQc6F3QdKUgLM= diff --git a/internal/testdata/migrations/sqlserver/20240528162241.sql b/internal/testdata/migrations/sqlserver/20240528162241.sql new file mode 100644 index 0000000..b1e6ff9 --- /dev/null +++ b/internal/testdata/migrations/sqlserver/20240528162241.sql @@ -0,0 +1,28 @@ +-- Create trigger "trg_insert_user_pet_history" +CREATE TRIGGER [trg_insert_user_pet_history] ON [pets] AFTER INSERT AS BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + SELECT + inserted.user_id, + inserted.id, + GETDATE() + FROM + inserted + WHERE + inserted.user_id IS NOT NULL; +END;; +-- Create trigger "trg_adding_heart_on_pet" +CREATE TRIGGER [trg_adding_heart_on_pet] ON [pets] INSTEAD OF INSERT AS BEGIN + INSERT INTO pets (name, user_id) + SELECT + CONCAT(inserted.name, ' ❤️'), + inserted.user_id + FROM + inserted; +END;; +-- Create "user_pet_histories" table +CREATE TABLE [user_pet_histories] ( + [user_id] bigint NOT NULL, + [pet_id] bigint NOT NULL, + [created_at] datetimeoffset(7) NULL, + CONSTRAINT [PK_user_pet_histories] PRIMARY KEY CLUSTERED ([user_id] ASC, [pet_id] ASC) +); diff --git a/internal/testdata/migrations/sqlserver/atlas.sum b/internal/testdata/migrations/sqlserver/atlas.sum index 6ec041b..5adbbf0 100644 --- a/internal/testdata/migrations/sqlserver/atlas.sum +++ b/internal/testdata/migrations/sqlserver/atlas.sum @@ -1,5 +1,5 @@ -h1:chvbQ1yysFYmq1K82/VfQeLxzMYrhjQ+8xv+2uoXdZw= +h1:Z65iGLRX5wuiESl5PSg7BjpLjYI9MzHN07ZVpJlwkPE= 20240124151658.sql h1:KaWALlql7BBV3oPVRT4rn+dvZaolhDmgbTgUPxIhauU= 20240512024328.sql h1:IBON1V3jlts+AqxRhejN82SE7/BIXSUWM0SQ0pvw4wc= 20240518091510.sql h1:CCFQHjVI+5dLXCgoPSERCHhyGBZl7QistZlrs1I5170= -20240601122612.sql h1:sCf/0g9oPsaj0Z3l7WO2BjthUwsK9OfPWv80J/L1WCA= +20240528162241.sql h1:csgs5bAZgZ53gtcPMbAVFlVSFb1tDWlfv3M+qx6RzZg= diff --git a/internal/testdata/models/pet.go b/internal/testdata/models/pet.go index bff753c..04cabb5 100644 --- a/internal/testdata/models/pet.go +++ b/internal/testdata/models/pet.go @@ -1,6 +1,8 @@ package models import ( + "time" + "gorm.io/gorm" "ariga.io/atlas-provider-gorm/gormschema" @@ -32,3 +34,100 @@ func (TopPetOwner) ViewDef(dialect string) []gormschema.ViewOption { } return []gormschema.ViewOption{gormschema.CreateStmt(stmt)} } + +type UserPetHistory struct { + UserID uint `gorm:"primaryKey"` + PetID uint `gorm:"primaryKey"` + CreatedAt time.Time +} + +func (Pet) Triggers(dialect string) [][]gormschema.TriggerOption { + var stmt1, stmt2 string + switch dialect { + case "mysql": + stmt1 = `CREATE TRIGGER trg_insert_user_pet_history +AFTER INSERT ON pets +FOR EACH ROW +BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + VALUES (NEW.user_id, NEW.id, NOW(3)); +END` + stmt2 = `CREATE TRIGGER trg_adding_heart_on_pet +BEFORE INSERT ON pets +FOR EACH ROW +BEGIN + SET NEW.name = CONCAT(NEW.name, ' ❤️'); +END` + case "sqlite": + stmt1 = `CREATE TRIGGER trg_insert_user_pet_history +AFTER INSERT ON pets +BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + VALUES (NEW.user_id, NEW.id, datetime('now')); +END` + stmt2 = `CREATE TRIGGER trg_adding_heart_on_pet +BEFORE INSERT ON pets +BEGIN + UPDATE pets SET name = name || ' ❤️' WHERE id = NEW.id; +END` + case "postgres": + stmt1 = `CREATE OR REPLACE FUNCTION log_user_pet_histories() +RETURNS TRIGGER AS $$ +BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + VALUES (NEW.user_id, NEW.id, NEW.created_at); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trg_insert_user_pet_history +AFTER INSERT ON pets +FOR EACH ROW +EXECUTE FUNCTION log_user_pet_histories();` + stmt2 = `CREATE OR REPLACE FUNCTION add_heart_on_pet() +RETURNS TRIGGER AS $$ +BEGIN + NEW.name := NEW.name || ' ❤️'; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trg_adding_heart_on_pet +BEFORE INSERT ON pets +FOR EACH ROW +EXECUTE FUNCTION add_heart_on_pet();` + case "sqlserver": + stmt1 = `CREATE TRIGGER trg_insert_user_pet_history +ON pets +AFTER INSERT +AS +BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + SELECT + inserted.user_id, + inserted.id, + GETDATE() + FROM + inserted + WHERE + inserted.user_id IS NOT NULL; +END` + stmt2 = `CREATE TRIGGER trg_adding_heart_on_pet +ON pets +INSTEAD OF INSERT +AS +BEGIN + INSERT INTO pets (name, user_id) + SELECT + CONCAT(inserted.name, ' ❤️'), + inserted.user_id + FROM + inserted; +END` + } + + return [][]gormschema.TriggerOption{ + []gormschema.TriggerOption{gormschema.CreateStmt(stmt1)}, + []gormschema.TriggerOption{gormschema.CreateStmt(stmt2)}, + } +} From 4488da105feafa9a315c7730bbfa00d96711b2f3 Mon Sep 17 00:00:00 2001 From: Luan Tran Date: Tue, 28 May 2024 23:37:17 +0700 Subject: [PATCH 05/11] docs: update usage for trigger --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index dd74875..9c3c523 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,7 @@ env "gorm" { > Note: Views are available for logged-in users, run `atlas login` if you haven't already. To learn more about logged-in features for Atlas, visit [Feature Availability](https://atlasgo.io/features#database-features). To define a Go struct as a database `VIEW`, implement the `ViewDef` method as follow: + ```go // User is a regular gorm.Model stored in the "users" table. type User struct { @@ -158,7 +159,9 @@ func (WorkingAgedUsers) ViewDef(dialect string) []gormschema.ViewOption { } } ``` + In order to pass a plain `CREATE VIEW` statement, use the `CreateStmt` as follows: + ```go type BotlTracker struct { ID uint @@ -176,19 +179,48 @@ func (BotlTracker) ViewDef(dialect string) []gormschema.ViewOption { } } ``` + To include both VIEWs and TABLEs in the migration generation, pass all models to the `Load` function: + ```go stmts, err := gormschema.New("mysql").Load( &models.User{}, // Table-based model. &models.WorkingAgedUsers{}, // View-based model. ) ``` + The view-based model works just like a regular models in GORM queries. However, make sure the view name is identical to the struct name, and in case they are differ, configure the name using the `TableName` method: + ```go func (WorkingAgedUsers) TableName() string { return "working_aged_users_custom_name" // View name is different than pluralized struct name. } ``` + +#### Trigger + +> Note: Trigger feature is only available for logged-in users, run `atlas login` if you haven't already. To learn more about logged-in features for Atlas, visit [Feature Availability](https://atlasgo.io/features#database-features). + +To attach triggers to a table, use the `Triggers(string)` method as follows: + +```go +type Pet struct { + gorm.Model + Name string +} + +func (Pet) Triggers(dialect string) [][]gormschema.TriggerOption { + var stmt string + switch dialect { + case "mysql": + stmt = "CREATE TRIGGER pet_insert BEFORE INSERT ON pets FOR EACH ROW SET NEW.name = UPPER(NEW.name)" + } + return [][]gormschema.TriggerOption{ + {gormschema.CreateTrigger(stmt)}, + } +} +``` + ### Additional Configuration To supply custom `gorm.Config{}` object to the provider use the [Go Program Mode](#as-go-file) with From a2a065e71c5e2916e47e150ceaa44ac2df5b960c Mon Sep 17 00:00:00 2001 From: luantranminh Date: Tue, 2 Jul 2024 01:22:46 +0700 Subject: [PATCH 06/11] refactor: change creating trigger API --- gormschema/gorm.go | 15 +++++++++++---- gormschema/gorm_test.go | 1 - gormschema/testdata/mysql_deterministic_output | 14 ++++++++++++++ gormschema/testdata/sqlite_no_fk | 2 ++ internal/testdata/models/pet.go | 8 ++++---- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/gormschema/gorm.go b/gormschema/gorm.go index d8fb50a..0a75132 100644 --- a/gormschema/gorm.go +++ b/gormschema/gorm.go @@ -36,6 +36,9 @@ type ( isTriggerOption() apply(*schemaBuilder) } + Trigger struct { + opts []TriggerOption + } // ViewDefiner defines a view. ViewDefiner interface { ViewDef(dialect string) []ViewOption @@ -77,6 +80,10 @@ func New(dialect string, opts ...Option) *Loader { return l } +func NewTrigger(opts ...TriggerOption) Trigger { + return Trigger{opts: opts} +} + func (s schemaOption) apply(b *schemaBuilder) { s(b) } @@ -364,14 +371,14 @@ func (m *migrator) orderModels(models ...any) ([]any, error) { func (m *migrator) CreateTriggers(models []any) error { for _, model := range models { if md, ok := model.(interface { - Triggers(string) [][]TriggerOption + Triggers(string) []Trigger }); ok { - for _, options := range md.Triggers(m.Dialector.Name()) { + for _, trigger := range md.Triggers(m.Dialector.Name()) { schemaBuilder := &schemaBuilder{ db: m.DB, } - for _, option := range options { - option.apply(schemaBuilder) + for _, opt := range trigger.opts { + opt.apply(schemaBuilder) if err := m.DB.Exec(schemaBuilder.createStmt).Error; err != nil { return err } diff --git a/gormschema/gorm_test.go b/gormschema/gorm_test.go index 8dfe8b8..93fdae8 100644 --- a/gormschema/gorm_test.go +++ b/gormschema/gorm_test.go @@ -20,7 +20,6 @@ func TestSQLiteConfig(t *testing.T) { models.WorkingAgedUsers{}, models.Pet{}, models.UserPetHistory{}, - models.User{}, ckmodels.Event{}, ckmodels.Location{}, models.TopPetOwner{}, diff --git a/gormschema/testdata/mysql_deterministic_output b/gormschema/testdata/mysql_deterministic_output index 9feb1df..b298dcd 100644 --- a/gormschema/testdata/mysql_deterministic_output +++ b/gormschema/testdata/mysql_deterministic_output @@ -2,8 +2,22 @@ CREATE TABLE `hobbies` (`id` bigint unsigned AUTO_INCREMENT,`name` longtext,PRIM CREATE TABLE `users` (`id` bigint unsigned AUTO_INCREMENT,`created_at` datetime(3) NULL,`updated_at` datetime(3) NULL,`deleted_at` datetime(3) NULL,`name` longtext,`age` bigint,PRIMARY KEY (`id`),INDEX `idx_users_deleted_at` (`deleted_at`)); CREATE TABLE `user_hobbies` (`user_id` bigint unsigned,`hobby_id` bigint unsigned,PRIMARY KEY (`user_id`,`hobby_id`)); CREATE TABLE `pets` (`id` bigint unsigned AUTO_INCREMENT,`created_at` datetime(3) NULL,`updated_at` datetime(3) NULL,`deleted_at` datetime(3) NULL,`name` longtext,`user_id` bigint unsigned,PRIMARY KEY (`id`),INDEX `idx_pets_deleted_at` (`deleted_at`)); +CREATE TABLE `user_pet_histories` (`user_id` bigint unsigned,`pet_id` bigint unsigned,`created_at` datetime(3) NULL,PRIMARY KEY (`user_id`,`pet_id`)); CREATE VIEW top_pet_owners AS SELECT user_id, COUNT(id) AS pet_count FROM pets GROUP BY user_id ORDER BY pet_count DESC LIMIT 10; CREATE VIEW working_aged_users AS SELECT name, age FROM `users` WHERE age BETWEEN 18 AND 65; +CREATE TRIGGER trg_insert_user_pet_history +AFTER INSERT ON pets +FOR EACH ROW +BEGIN + INSERT INTO user_pet_histories (user_id, pet_id, created_at) + VALUES (NEW.user_id, NEW.id, NOW(3)); +END; +CREATE TRIGGER trg_adding_heart_on_pet +BEFORE INSERT ON pets +FOR EACH ROW +BEGIN + SET NEW.name = CONCAT(NEW.name, ' ❤️'); +END; ALTER TABLE `user_hobbies` ADD CONSTRAINT `fk_user_hobbies_hobby` FOREIGN KEY (`hobby_id`) REFERENCES `hobbies`(`id`); ALTER TABLE `user_hobbies` ADD CONSTRAINT `fk_user_hobbies_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`); ALTER TABLE `pets` ADD CONSTRAINT `fk_users_pets` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`); diff --git a/gormschema/testdata/sqlite_no_fk b/gormschema/testdata/sqlite_no_fk index eb94019..210ea63 100644 --- a/gormschema/testdata/sqlite_no_fk +++ b/gormschema/testdata/sqlite_no_fk @@ -3,6 +3,8 @@ CREATE TABLE `users` (`id` integer,`created_at` datetime,`updated_at` datetime,` CREATE INDEX `idx_users_deleted_at` ON `users`(`deleted_at`); CREATE TABLE `pets` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`name` text,`user_id` integer,PRIMARY KEY (`id`)); CREATE INDEX `idx_pets_deleted_at` ON `pets`(`deleted_at`); +CREATE TABLE `hobbies` (`id` integer,`name` text,PRIMARY KEY (`id`)); +CREATE TABLE `user_hobbies` (`hobby_id` integer,`user_id` integer,PRIMARY KEY (`hobby_id`,`user_id`)); CREATE TRIGGER trg_insert_user_pet_history AFTER INSERT ON pets BEGIN diff --git a/internal/testdata/models/pet.go b/internal/testdata/models/pet.go index 04cabb5..2e13341 100644 --- a/internal/testdata/models/pet.go +++ b/internal/testdata/models/pet.go @@ -41,7 +41,7 @@ type UserPetHistory struct { CreatedAt time.Time } -func (Pet) Triggers(dialect string) [][]gormschema.TriggerOption { +func (Pet) Triggers(dialect string) []gormschema.Trigger { var stmt1, stmt2 string switch dialect { case "mysql": @@ -126,8 +126,8 @@ BEGIN END` } - return [][]gormschema.TriggerOption{ - []gormschema.TriggerOption{gormschema.CreateStmt(stmt1)}, - []gormschema.TriggerOption{gormschema.CreateStmt(stmt2)}, + return []gormschema.Trigger{ + gormschema.NewTrigger(gormschema.CreateStmt(stmt1)), + gormschema.NewTrigger(gormschema.CreateStmt(stmt2)), } } From e5a7fdf7bf0a572ee892a5266208f0dce3c0672c Mon Sep 17 00:00:00 2001 From: luantranminh Date: Tue, 2 Jul 2024 21:52:55 +0700 Subject: [PATCH 07/11] chore: re-hash atlas.sum --- internal/testdata/migrations/mysql/atlas.sum | 3 ++- internal/testdata/migrations/postgres/atlas.sum | 3 ++- internal/testdata/migrations/sqlite/atlas.sum | 3 ++- internal/testdata/migrations/sqlserver/atlas.sum | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/testdata/migrations/mysql/atlas.sum b/internal/testdata/migrations/mysql/atlas.sum index f6a1bc8..9f8072f 100644 --- a/internal/testdata/migrations/mysql/atlas.sum +++ b/internal/testdata/migrations/mysql/atlas.sum @@ -1,5 +1,6 @@ -h1:k7/CcbTblLR2glTHovuXUh3GeYYyJOPF92p9JTARekQ= +h1:Gf/TDXiqnCBXtXup2xsblQ5jaEdmk8WXM+nGabF/H2A= 20230627123246.sql h1:+bgzC3WJyyIR6Rv/FUvaNXJ1gkbKJlYcEMgp69yORIY= 20240512024238.sql h1:2kQL4tE/tAhvXuozmRAJ3oXTo1KRz11QosVDw+0va14= 20240518091603.sql h1:xNClqqRaOjXwg0julpsiPYsmqUcEL/hJel1iqYzi3DM= 20240528162003.sql h1:uNDd913fXzlpf8xgLiLsfCaoeZsbQkGPfZ5DXQOXVaU= +20240601122756.sql h1:aq5VTDQTfq0ggGNDPRkWs0dRqBz4Kf9qfwjiIqqF7Mk= diff --git a/internal/testdata/migrations/postgres/atlas.sum b/internal/testdata/migrations/postgres/atlas.sum index 778890e..d54049d 100644 --- a/internal/testdata/migrations/postgres/atlas.sum +++ b/internal/testdata/migrations/postgres/atlas.sum @@ -1,5 +1,6 @@ -h1:DmCb55HYPLacSRaYyN6i+dbYVjh2so0p/xGJgkQY4Nw= +h1:OMiKZx2H7UFTavabYyXOKaNezT8TjXwn8J/VSWK+Poo= 20230627123049.sql h1:1jYJM2+VCr9152vg6gayCrcEvuT/FE7ufOyZ86VLaOE= 20240512024223.sql h1:RY4w148OJBBr5sXpfBq6B48p/1cFrTEpH4TlwD7mUps= 20240518091611.sql h1:3Kv6mYS8ML72H6HE5qr/a2gdVUrfWuHVufP/1wl70vE= 20240528162135.sql h1:3KF6tW9nyVF010sKTm3pRN9rtuXTTjOx0uG+SMaVim0= +20240601122813.sql h1:XyQjDYX8gSyXh7CiYubY45JnV+tMvBPExxo8znkua2U= diff --git a/internal/testdata/migrations/sqlite/atlas.sum b/internal/testdata/migrations/sqlite/atlas.sum index ab530b8..5162736 100644 --- a/internal/testdata/migrations/sqlite/atlas.sum +++ b/internal/testdata/migrations/sqlite/atlas.sum @@ -1,5 +1,6 @@ -h1:9ZIWiyIkB+4j2ihwBIL60jZq7j4EPesQQv04/sb4SQc= +h1:VYB+vkYQ3e/FUxyPE8ObeGAoDMfw75/HElpFItgigDc= 20230627123228.sql h1:YfwJdN73sWz1G5/0tU2BtGLyzJCfRQr8blTSquUZ+qo= 20240511123637.sql h1:Kbk3wUzTfBbq8mDdTT08hP93ecNU0y5oTL+O8idEcdQ= 20240518091437.sql h1:svMANRZuZDvgzqO3iyNLUjrUrK8BTMEB2f0NV3d5sJo= 20240528162330.sql h1:G8xUMjRgqGYYuDElT6PdNj/0k2HR+xYQc6F3QdKUgLM= +20240601122746.sql h1:9hcfynWkLTvpq+sHgqphJCB5zUmyZhsLBcODo3jFb1I= diff --git a/internal/testdata/migrations/sqlserver/atlas.sum b/internal/testdata/migrations/sqlserver/atlas.sum index 5adbbf0..7382d90 100644 --- a/internal/testdata/migrations/sqlserver/atlas.sum +++ b/internal/testdata/migrations/sqlserver/atlas.sum @@ -1,5 +1,6 @@ -h1:Z65iGLRX5wuiESl5PSg7BjpLjYI9MzHN07ZVpJlwkPE= +h1:z9pqntDdIvzabj+Hm/naUhpkJ7Y/ndgmDQlGSQXv8L8= 20240124151658.sql h1:KaWALlql7BBV3oPVRT4rn+dvZaolhDmgbTgUPxIhauU= 20240512024328.sql h1:IBON1V3jlts+AqxRhejN82SE7/BIXSUWM0SQ0pvw4wc= 20240518091510.sql h1:CCFQHjVI+5dLXCgoPSERCHhyGBZl7QistZlrs1I5170= 20240528162241.sql h1:csgs5bAZgZ53gtcPMbAVFlVSFb1tDWlfv3M+qx6RzZg= +20240601122612.sql h1:9PysaU11aZe1XPxiSEEUbY2tJGDjyMo8f/LYHNZgiqg= From 3cb02d51b1186ff0c5f9961641c2609ee78e42c7 Mon Sep 17 00:00:00 2001 From: luantranminh Date: Tue, 2 Jul 2024 22:02:23 +0700 Subject: [PATCH 08/11] fix: remove unicode character --- gormschema/testdata/mysql_default | 2 +- gormschema/testdata/mysql_deterministic_output | 2 +- gormschema/testdata/postgresql_default | 2 +- gormschema/testdata/sqlite_default | 2 +- gormschema/testdata/sqlite_no_fk | 2 +- gormschema/testdata/sqlserver_default | 2 +- internal/testdata/migrations/mysql/20240528162003.sql | 2 +- internal/testdata/migrations/mysql/atlas.sum | 6 +++--- internal/testdata/migrations/postgres/20240528162135.sql | 2 +- internal/testdata/migrations/postgres/atlas.sum | 6 +++--- internal/testdata/migrations/sqlite/20240528162330.sql | 2 +- internal/testdata/migrations/sqlite/atlas.sum | 6 +++--- internal/testdata/migrations/sqlserver/20240528162241.sql | 2 +- internal/testdata/migrations/sqlserver/atlas.sum | 6 +++--- internal/testdata/models/pet.go | 8 ++++---- 15 files changed, 26 insertions(+), 26 deletions(-) diff --git a/gormschema/testdata/mysql_default b/gormschema/testdata/mysql_default index dad5674..87f336f 100644 --- a/gormschema/testdata/mysql_default +++ b/gormschema/testdata/mysql_default @@ -18,7 +18,7 @@ CREATE TRIGGER trg_adding_heart_on_pet BEFORE INSERT ON pets FOR EACH ROW BEGIN - SET NEW.name = CONCAT(NEW.name, ' ❤️'); + SET NEW.name = CONCAT(NEW.name, ' <3'); END; ALTER TABLE `events` ADD CONSTRAINT `fk_locations_event` FOREIGN KEY (`locationId`) REFERENCES `locations`(`locationId`); ALTER TABLE `locations` ADD CONSTRAINT `fk_events_location` FOREIGN KEY (`eventId`) REFERENCES `events`(`eventId`); diff --git a/gormschema/testdata/mysql_deterministic_output b/gormschema/testdata/mysql_deterministic_output index b298dcd..c41114f 100644 --- a/gormschema/testdata/mysql_deterministic_output +++ b/gormschema/testdata/mysql_deterministic_output @@ -16,7 +16,7 @@ CREATE TRIGGER trg_adding_heart_on_pet BEFORE INSERT ON pets FOR EACH ROW BEGIN - SET NEW.name = CONCAT(NEW.name, ' ❤️'); + SET NEW.name = CONCAT(NEW.name, ' <3'); END; ALTER TABLE `user_hobbies` ADD CONSTRAINT `fk_user_hobbies_hobby` FOREIGN KEY (`hobby_id`) REFERENCES `hobbies`(`id`); ALTER TABLE `user_hobbies` ADD CONSTRAINT `fk_user_hobbies_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`); diff --git a/gormschema/testdata/postgresql_default b/gormschema/testdata/postgresql_default index 79fc6f0..60fd2d6 100644 --- a/gormschema/testdata/postgresql_default +++ b/gormschema/testdata/postgresql_default @@ -27,7 +27,7 @@ EXECUTE FUNCTION log_user_pet_histories();; CREATE OR REPLACE FUNCTION add_heart_on_pet() RETURNS TRIGGER AS $$ BEGIN - NEW.name := NEW.name || ' ❤️'; + NEW.name := NEW.name || ' <3'; RETURN NEW; END; $$ LANGUAGE plpgsql; diff --git a/gormschema/testdata/sqlite_default b/gormschema/testdata/sqlite_default index 2a035b1..5600293 100644 --- a/gormschema/testdata/sqlite_default +++ b/gormschema/testdata/sqlite_default @@ -18,5 +18,5 @@ END; CREATE TRIGGER trg_adding_heart_on_pet BEFORE INSERT ON pets BEGIN - UPDATE pets SET name = name || ' ❤️' WHERE id = NEW.id; + UPDATE pets SET name = name || ' <3' WHERE id = NEW.id; END; diff --git a/gormschema/testdata/sqlite_no_fk b/gormschema/testdata/sqlite_no_fk index 210ea63..fb62820 100644 --- a/gormschema/testdata/sqlite_no_fk +++ b/gormschema/testdata/sqlite_no_fk @@ -14,5 +14,5 @@ END; CREATE TRIGGER trg_adding_heart_on_pet BEFORE INSERT ON pets BEGIN - UPDATE pets SET name = name || ' ❤️' WHERE id = NEW.id; + UPDATE pets SET name = name || ' <3' WHERE id = NEW.id; END; diff --git a/gormschema/testdata/sqlserver_default b/gormschema/testdata/sqlserver_default index 7f19601..7fc5138 100644 --- a/gormschema/testdata/sqlserver_default +++ b/gormschema/testdata/sqlserver_default @@ -33,7 +33,7 @@ AS BEGIN INSERT INTO pets (name, user_id) SELECT - CONCAT(inserted.name, ' ❤️'), + CONCAT(inserted.name, ' <3'), inserted.user_id FROM inserted; diff --git a/internal/testdata/migrations/mysql/20240528162003.sql b/internal/testdata/migrations/mysql/20240528162003.sql index f87479c..283851c 100644 --- a/internal/testdata/migrations/mysql/20240528162003.sql +++ b/internal/testdata/migrations/mysql/20240528162003.sql @@ -1,6 +1,6 @@ -- Create trigger "trg_adding_heart_on_pet" CREATE TRIGGER `trg_adding_heart_on_pet` BEFORE INSERT ON `pets` FOR EACH ROW BEGIN - SET NEW.name = CONCAT(NEW.name, ' ❤️'); + SET NEW.name = CONCAT(NEW.name, ' <3'); END; -- Create trigger "trg_insert_user_pet_history" CREATE TRIGGER `trg_insert_user_pet_history` AFTER INSERT ON `pets` FOR EACH ROW BEGIN diff --git a/internal/testdata/migrations/mysql/atlas.sum b/internal/testdata/migrations/mysql/atlas.sum index 9f8072f..372aa8e 100644 --- a/internal/testdata/migrations/mysql/atlas.sum +++ b/internal/testdata/migrations/mysql/atlas.sum @@ -1,6 +1,6 @@ -h1:Gf/TDXiqnCBXtXup2xsblQ5jaEdmk8WXM+nGabF/H2A= +h1:SewVf3c3uzZ3RukSbEFfrhVO7qP3fXNNZQPNSbyVm8Y= 20230627123246.sql h1:+bgzC3WJyyIR6Rv/FUvaNXJ1gkbKJlYcEMgp69yORIY= 20240512024238.sql h1:2kQL4tE/tAhvXuozmRAJ3oXTo1KRz11QosVDw+0va14= 20240518091603.sql h1:xNClqqRaOjXwg0julpsiPYsmqUcEL/hJel1iqYzi3DM= -20240528162003.sql h1:uNDd913fXzlpf8xgLiLsfCaoeZsbQkGPfZ5DXQOXVaU= -20240601122756.sql h1:aq5VTDQTfq0ggGNDPRkWs0dRqBz4Kf9qfwjiIqqF7Mk= +20240528162003.sql h1:t0jcB27MMyDFHC454jyy/ODE4K1H/Ekwg+7stba0TEQ= +20240601122756.sql h1:9NVhx5ubGsIZYOiXY2GSV1PcZQxrxL2F12TM/T4jRBA= diff --git a/internal/testdata/migrations/postgres/20240528162135.sql b/internal/testdata/migrations/postgres/20240528162135.sql index d356908..a215579 100644 --- a/internal/testdata/migrations/postgres/20240528162135.sql +++ b/internal/testdata/migrations/postgres/20240528162135.sql @@ -11,7 +11,7 @@ CREATE TRIGGER "trg_insert_user_pet_history" AFTER INSERT ON "public"."pets" FOR -- Create "add_heart_on_pet" function CREATE FUNCTION "public"."add_heart_on_pet" () RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN - NEW.name := NEW.name || ' ❤️'; + NEW.name := NEW.name || ' <3'; RETURN NEW; END; $$; diff --git a/internal/testdata/migrations/postgres/atlas.sum b/internal/testdata/migrations/postgres/atlas.sum index d54049d..fb6d4b1 100644 --- a/internal/testdata/migrations/postgres/atlas.sum +++ b/internal/testdata/migrations/postgres/atlas.sum @@ -1,6 +1,6 @@ -h1:OMiKZx2H7UFTavabYyXOKaNezT8TjXwn8J/VSWK+Poo= +h1:004to5XhdkOrbE16ahrCO3rJoNEsUFkBtP6jtchr6Jo= 20230627123049.sql h1:1jYJM2+VCr9152vg6gayCrcEvuT/FE7ufOyZ86VLaOE= 20240512024223.sql h1:RY4w148OJBBr5sXpfBq6B48p/1cFrTEpH4TlwD7mUps= 20240518091611.sql h1:3Kv6mYS8ML72H6HE5qr/a2gdVUrfWuHVufP/1wl70vE= -20240528162135.sql h1:3KF6tW9nyVF010sKTm3pRN9rtuXTTjOx0uG+SMaVim0= -20240601122813.sql h1:XyQjDYX8gSyXh7CiYubY45JnV+tMvBPExxo8znkua2U= +20240528162135.sql h1:b8wlzdKR7j6se8BCHEYxQ+fOU1weAZ18rhnH6vvWzFw= +20240601122813.sql h1:y8B6XrUIXYWNxUCtxP6Tx/SZ+WHH3WsFD6BnfLaCwJc= diff --git a/internal/testdata/migrations/sqlite/20240528162330.sql b/internal/testdata/migrations/sqlite/20240528162330.sql index e902905..b07f69c 100644 --- a/internal/testdata/migrations/sqlite/20240528162330.sql +++ b/internal/testdata/migrations/sqlite/20240528162330.sql @@ -5,7 +5,7 @@ CREATE TRIGGER `trg_insert_user_pet_history` AFTER INSERT ON `pets` FOR EACH ROW END; -- Create trigger "trg_adding_heart_on_pet" CREATE TRIGGER `trg_adding_heart_on_pet` BEFORE INSERT ON `pets` FOR EACH ROW BEGIN - UPDATE pets SET name = name || ' ❤️' WHERE id = NEW.id; + UPDATE pets SET name = name || ' <3' WHERE id = NEW.id; -- Create "user_pet_histories" table CREATE TABLE `user_pet_histories` ( `user_id` integer NULL, diff --git a/internal/testdata/migrations/sqlite/atlas.sum b/internal/testdata/migrations/sqlite/atlas.sum index 5162736..5dad70f 100644 --- a/internal/testdata/migrations/sqlite/atlas.sum +++ b/internal/testdata/migrations/sqlite/atlas.sum @@ -1,6 +1,6 @@ -h1:VYB+vkYQ3e/FUxyPE8ObeGAoDMfw75/HElpFItgigDc= +h1:Y7WZi7LOLafr50qB8QD/YCQm5TZrWBbB5xEmpebRfHA= 20230627123228.sql h1:YfwJdN73sWz1G5/0tU2BtGLyzJCfRQr8blTSquUZ+qo= 20240511123637.sql h1:Kbk3wUzTfBbq8mDdTT08hP93ecNU0y5oTL+O8idEcdQ= 20240518091437.sql h1:svMANRZuZDvgzqO3iyNLUjrUrK8BTMEB2f0NV3d5sJo= -20240528162330.sql h1:G8xUMjRgqGYYuDElT6PdNj/0k2HR+xYQc6F3QdKUgLM= -20240601122746.sql h1:9hcfynWkLTvpq+sHgqphJCB5zUmyZhsLBcODo3jFb1I= +20240528162330.sql h1:Bz102YLUYHxPDNbs+qgHQ/FKohxUXaBuQPPzsR5QNOE= +20240601122746.sql h1:T9RNFZSrKvE15ji/CvBDoV3WIcS5WJjVEV4xWHNl0JY= diff --git a/internal/testdata/migrations/sqlserver/20240528162241.sql b/internal/testdata/migrations/sqlserver/20240528162241.sql index b1e6ff9..0bd6f51 100644 --- a/internal/testdata/migrations/sqlserver/20240528162241.sql +++ b/internal/testdata/migrations/sqlserver/20240528162241.sql @@ -14,7 +14,7 @@ END;; CREATE TRIGGER [trg_adding_heart_on_pet] ON [pets] INSTEAD OF INSERT AS BEGIN INSERT INTO pets (name, user_id) SELECT - CONCAT(inserted.name, ' ❤️'), + CONCAT(inserted.name, ' <3'), inserted.user_id FROM inserted; diff --git a/internal/testdata/migrations/sqlserver/atlas.sum b/internal/testdata/migrations/sqlserver/atlas.sum index 7382d90..d1a799c 100644 --- a/internal/testdata/migrations/sqlserver/atlas.sum +++ b/internal/testdata/migrations/sqlserver/atlas.sum @@ -1,6 +1,6 @@ -h1:z9pqntDdIvzabj+Hm/naUhpkJ7Y/ndgmDQlGSQXv8L8= +h1:pbVtWu6feRtoRq90DbpxEC3hUK6MdCaZvh8F5kWibAs= 20240124151658.sql h1:KaWALlql7BBV3oPVRT4rn+dvZaolhDmgbTgUPxIhauU= 20240512024328.sql h1:IBON1V3jlts+AqxRhejN82SE7/BIXSUWM0SQ0pvw4wc= 20240518091510.sql h1:CCFQHjVI+5dLXCgoPSERCHhyGBZl7QistZlrs1I5170= -20240528162241.sql h1:csgs5bAZgZ53gtcPMbAVFlVSFb1tDWlfv3M+qx6RzZg= -20240601122612.sql h1:9PysaU11aZe1XPxiSEEUbY2tJGDjyMo8f/LYHNZgiqg= +20240528162241.sql h1:/HGF+9kwa+4jGCT4gidpNxdRAB02wn//GtMqZ7j1DOg= +20240601122612.sql h1:ff7/8377CAqwdWAoceY/A4tYlLR1mqJxTUpLdCagDnM= diff --git a/internal/testdata/models/pet.go b/internal/testdata/models/pet.go index 2e13341..3685be2 100644 --- a/internal/testdata/models/pet.go +++ b/internal/testdata/models/pet.go @@ -56,7 +56,7 @@ END` BEFORE INSERT ON pets FOR EACH ROW BEGIN - SET NEW.name = CONCAT(NEW.name, ' ❤️'); + SET NEW.name = CONCAT(NEW.name, ' <3'); END` case "sqlite": stmt1 = `CREATE TRIGGER trg_insert_user_pet_history @@ -68,7 +68,7 @@ END` stmt2 = `CREATE TRIGGER trg_adding_heart_on_pet BEFORE INSERT ON pets BEGIN - UPDATE pets SET name = name || ' ❤️' WHERE id = NEW.id; + UPDATE pets SET name = name || ' <3' WHERE id = NEW.id; END` case "postgres": stmt1 = `CREATE OR REPLACE FUNCTION log_user_pet_histories() @@ -87,7 +87,7 @@ EXECUTE FUNCTION log_user_pet_histories();` stmt2 = `CREATE OR REPLACE FUNCTION add_heart_on_pet() RETURNS TRIGGER AS $$ BEGIN - NEW.name := NEW.name || ' ❤️'; + NEW.name := NEW.name || ' <3'; RETURN NEW; END; $$ LANGUAGE plpgsql; @@ -119,7 +119,7 @@ AS BEGIN INSERT INTO pets (name, user_id) SELECT - CONCAT(inserted.name, ' ❤️'), + CONCAT(inserted.name, ' <3'), inserted.user_id FROM inserted; From c7548ecc528a86a353359f180865b9dd7a9aa5de Mon Sep 17 00:00:00 2001 From: luantranminh Date: Tue, 2 Jul 2024 22:24:09 +0700 Subject: [PATCH 09/11] fix: correct sqlite migration files --- internal/testdata/migrations/sqlite/20240528162330.sql | 1 + internal/testdata/migrations/sqlite/atlas.sum | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/testdata/migrations/sqlite/20240528162330.sql b/internal/testdata/migrations/sqlite/20240528162330.sql index b07f69c..f334771 100644 --- a/internal/testdata/migrations/sqlite/20240528162330.sql +++ b/internal/testdata/migrations/sqlite/20240528162330.sql @@ -6,6 +6,7 @@ END; -- Create trigger "trg_adding_heart_on_pet" CREATE TRIGGER `trg_adding_heart_on_pet` BEFORE INSERT ON `pets` FOR EACH ROW BEGIN UPDATE pets SET name = name || ' <3' WHERE id = NEW.id; +END; -- Create "user_pet_histories" table CREATE TABLE `user_pet_histories` ( `user_id` integer NULL, diff --git a/internal/testdata/migrations/sqlite/atlas.sum b/internal/testdata/migrations/sqlite/atlas.sum index 5dad70f..3f80136 100644 --- a/internal/testdata/migrations/sqlite/atlas.sum +++ b/internal/testdata/migrations/sqlite/atlas.sum @@ -1,6 +1,6 @@ -h1:Y7WZi7LOLafr50qB8QD/YCQm5TZrWBbB5xEmpebRfHA= +h1:ZcgtDL3lj/vDZHQaciSLUG78ptrqYeZyvavU5VKXdew= 20230627123228.sql h1:YfwJdN73sWz1G5/0tU2BtGLyzJCfRQr8blTSquUZ+qo= 20240511123637.sql h1:Kbk3wUzTfBbq8mDdTT08hP93ecNU0y5oTL+O8idEcdQ= 20240518091437.sql h1:svMANRZuZDvgzqO3iyNLUjrUrK8BTMEB2f0NV3d5sJo= -20240528162330.sql h1:Bz102YLUYHxPDNbs+qgHQ/FKohxUXaBuQPPzsR5QNOE= -20240601122746.sql h1:T9RNFZSrKvE15ji/CvBDoV3WIcS5WJjVEV4xWHNl0JY= +20240528162330.sql h1:UHg9lcwzLRovMOUscLmG/Pe+0UYnTc/JcA2GgGawGNM= +20240601122746.sql h1:V2pXzBKor6b4HNxdYevk2QfFhDOSnVfTmoftPFp/x58= From cf78e0de9f6fed27b4058bb324ba81c9b5be1706 Mon Sep 17 00:00:00 2001 From: luantranminh Date: Tue, 2 Jul 2024 22:37:15 +0700 Subject: [PATCH 10/11] chore: add comments --- gormschema/gorm.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gormschema/gorm.go b/gormschema/gorm.go index 0a75132..01dda5c 100644 --- a/gormschema/gorm.go +++ b/gormschema/gorm.go @@ -36,6 +36,7 @@ type ( isTriggerOption() apply(*schemaBuilder) } + // Trigger defines a trigger. Trigger struct { opts []TriggerOption } @@ -80,6 +81,7 @@ func New(dialect string, opts ...Option) *Loader { return l } +// NewTrigger receives a list of TriggerOption to build a Trigger. func NewTrigger(opts ...TriggerOption) Trigger { return Trigger{opts: opts} } From 149b6381901a96ff5c024b30dc2f7fc12b9ac6f5 Mon Sep 17 00:00:00 2001 From: luantranminh Date: Tue, 2 Jul 2024 23:11:03 +0700 Subject: [PATCH 11/11] docs: change trigger usage --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9c3c523..bbd1815 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,7 @@ func (WorkingAgedUsers) TableName() string { > Note: Trigger feature is only available for logged-in users, run `atlas login` if you haven't already. To learn more about logged-in features for Atlas, visit [Feature Availability](https://atlasgo.io/features#database-features). -To attach triggers to a table, use the `Triggers(string)` method as follows: +To attach triggers to a table, use the `Triggers` method as follows: ```go type Pet struct { @@ -209,14 +209,14 @@ type Pet struct { Name string } -func (Pet) Triggers(dialect string) [][]gormschema.TriggerOption { +func (Pet) Triggers(dialect string) []gormschema.Trigger { var stmt string switch dialect { case "mysql": stmt = "CREATE TRIGGER pet_insert BEFORE INSERT ON pets FOR EACH ROW SET NEW.name = UPPER(NEW.name)" } - return [][]gormschema.TriggerOption{ - {gormschema.CreateTrigger(stmt)}, + return []gormschema.Trigger{ + gormschema.NewTrigger(gormschema.CreateStmt(stmt)), } } ```