diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 11a271e..9e756ae 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: - go-version: '1.20' + go-version-file: 'go.mod' - name: Run Go linters uses: golangci/golangci-lint-action@v3 with: @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: - go-version: '1.20' + go-version-file: 'go.mod' - name: Run tests run: go test -race ./... integration-test: @@ -35,12 +35,15 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: - go-version: '1.20' + go-version-file: 'go.mod' - run: | curl -sSf 'https://atlasgo.sh?test=1' | env ATLAS_DEBUG=true sh - working-directory: internal/testdata run: | atlas migrate diff --env gorm --var dialect=${{ matrix.dialect }} + - working-directory: internal/testdata/circularfks + run: | + atlas migrate diff --env gorm --var dialect=${{ matrix.dialect }} - name: Verify migrations generated run: | status=$(git status --porcelain) @@ -49,4 +52,4 @@ jobs: echo "$status" git --no-pager diff exit 1 - fi \ No newline at end of file + fi diff --git a/go.mod b/go.mod index c2371c9..7e2f8b9 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,11 @@ module ariga.io/atlas-provider-gorm -go 1.20 +go 1.21 + +toolchain go1.21.1 require ( - ariga.io/atlas-go-sdk v0.1.1-0.20231001054405-7edfcfc14f1c + ariga.io/atlas-go-sdk v0.2.3 github.com/alecthomas/kong v0.7.1 github.com/stretchr/testify v1.8.4 golang.org/x/tools v0.10.0 @@ -28,6 +30,6 @@ require ( golang.org/x/crypto v0.8.0 // indirect golang.org/x/mod v0.11.0 // indirect golang.org/x/sys v0.9.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/text v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ba18070..b36bfbd 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,11 @@ -ariga.io/atlas-go-sdk v0.0.0-20230709063453-1058d6508503 h1:D12EzAAjhL7xEJk5jFnkKYUXVrDQ4mh0IjB8vqTmo8I= -ariga.io/atlas-go-sdk v0.0.0-20230709063453-1058d6508503/go.mod h1:fwi5nIOFLedo6CqZ0a172dhykLWBnoD25bqmZhvW948= -ariga.io/atlas-go-sdk v0.1.0 h1:sSPV26Lv2DIzbc+oZxgnAqYgKEwBFU0FsFDUUnUNHHs= -ariga.io/atlas-go-sdk v0.1.0/go.mod h1:738TvNdlvLnkRhB+euXvrhKPJkeV+LPoVa4xarUGCaQ= -ariga.io/atlas-go-sdk v0.1.1-0.20231001054405-7edfcfc14f1c h1:jvi4KB/7DmYYT+Wy2TFImccaBU0+dw7V8Un67NDGuio= -ariga.io/atlas-go-sdk v0.1.1-0.20231001054405-7edfcfc14f1c/go.mod h1:MLvZ9QwZx1KhI6+8XguxHPUPm0/PTTUr46S5GQAe9WI= +ariga.io/atlas-go-sdk v0.2.3 h1:DpKruiJ9ElJcNhYxnQM9ddzupHXEYFH0Jx6ZcZ7lKYQ= +ariga.io/atlas-go-sdk v0.2.3/go.mod h1:owkEEXw6jqne5KPVDfKsYB7cwMiMk3jtOiAAeKxS/yU= github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0= +github.com/alecthomas/assert/v2 v2.1.0/go.mod h1:b/+1DI2Q6NckYi+3mXyH3wFb8qG37K/DuK80n7WefXA= github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4= github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE= +github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -15,6 +13,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= @@ -26,6 +25,7 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= @@ -44,14 +44,16 @@ golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/gormschema/gorm.go b/gormschema/gorm.go index f306b6d..051100e 100644 --- a/gormschema/gorm.go +++ b/gormschema/gorm.go @@ -3,6 +3,7 @@ package gormschema import ( "database/sql" "database/sql/driver" + "errors" "fmt" "ariga.io/atlas-go-sdk/recordriver" @@ -10,6 +11,7 @@ import ( "gorm.io/driver/postgres" "gorm.io/driver/sqlite" "gorm.io/gorm" + gormig "gorm.io/gorm/migrator" ) // New returns a new Loader. @@ -73,12 +75,82 @@ func (l *Loader) Load(models ...any) (string, error) { if err != nil { return "", err } - if err := db.AutoMigrate(models...); err != nil { + if l.dialect != "sqlite" { + db.Config.DisableForeignKeyConstraintWhenMigrating = true + } + if err = db.AutoMigrate(models...); err != nil { return "", err } + if !l.config.DisableForeignKeyConstraintWhenMigrating && l.dialect != "sqlite" { + db, err = gorm.Open(dialector{ + Dialector: di, + }, l.config) + if err != nil { + return "", err + } + cm, ok := db.Migrator().(*migrator) + if !ok { + return "", err + } + if err = cm.CreateConstraints(models); err != nil { + return "", err + } + } s, ok := recordriver.Session("gorm") if !ok { - return "", err + return "", errors.New("gorm db session not found") } return s.Stmts(), nil } + +type migrator struct { + gormig.Migrator + dialectMigrator gorm.Migrator +} + +type dialector struct { + gorm.Dialector +} + +// Migrator returns a new gorm.Migrator which can be used to automatically create all Constraints +// on existing tables. +func (d dialector) Migrator(db *gorm.DB) gorm.Migrator { + return &migrator{ + Migrator: gormig.Migrator{ + Config: gormig.Config{ + DB: db, + Dialector: d, + }, + }, + dialectMigrator: d.Dialector.Migrator(db), + } +} + +// HasTable always returns `true`. By returning `true`, gorm.Migrator will try to alter the table to add constraints. +func (m *migrator) HasTable(dst interface{}) bool { + return true +} + +// CreateConstraints detects constraints on the given model and creates them using `m.dialectMigrator`. +func (m *migrator) CreateConstraints(models []interface{}) error { + for _, model := range m.ReorderModels(models, true) { + err := m.Migrator.RunWithValue(model, func(stmt *gorm.Statement) error { + for _, rel := range stmt.Schema.Relationships.Relations { + if rel.Field.IgnoreMigration { + continue + } + if constraint := rel.ParseConstraint(); constraint != nil && + constraint.Schema == stmt.Schema { + if err := m.dialectMigrator.CreateConstraint(model, constraint.Name); err != nil { + return err + } + } + } + return nil + }) + if err != nil { + return err + } + } + return nil +} diff --git a/gormschema/gorm_test.go b/gormschema/gorm_test.go index a929156..af209d2 100644 --- a/gormschema/gorm_test.go +++ b/gormschema/gorm_test.go @@ -1,22 +1,74 @@ package gormschema import ( + "os" "testing" + "ariga.io/atlas-go-sdk/recordriver" + ckmodels "ariga.io/atlas-provider-gorm/internal/testdata/circularfks" "ariga.io/atlas-provider-gorm/internal/testdata/models" "github.com/stretchr/testify/require" "gorm.io/gorm" ) -func TestConfig(t *testing.T) { - l := New("sqlite", WithConfig( +func TestSQLiteConfig(t *testing.T) { + resetSession() + l := New("sqlite") + sql, err := l.Load(models.Pet{}, models.User{}, ckmodels.Event{}, ckmodels.Location{}) + require.NoError(t, err) + requireEqualContent(t, sql, "testdata/sqlite_default") + resetSession() + l = New("sqlite", WithConfig(&gorm.Config{ + DisableForeignKeyConstraintWhenMigrating: true, + })) + sql, err = l.Load(models.Pet{}, models.User{}) + require.NoError(t, err) + requireEqualContent(t, sql, "testdata/sqlite_no_fk") + resetSession() +} + +func TestPostgreSQLConfig(t *testing.T) { + resetSession() + l := New("postgres") + sql, err := l.Load(ckmodels.Location{}, ckmodels.Event{}, models.User{}, models.Pet{}) + require.NoError(t, err) + requireEqualContent(t, sql, "testdata/postgresql_default") + resetSession() + l = New("postgres", WithConfig( + &gorm.Config{ + DisableForeignKeyConstraintWhenMigrating: true, + })) + sql, err = l.Load(ckmodels.Location{}, ckmodels.Event{}) + require.NoError(t, err) + requireEqualContent(t, sql, "testdata/postgresql_no_fk") +} + +func TestMySQLConfig(t *testing.T) { + resetSession() + l := New("mysql") + sql, err := l.Load(ckmodels.Location{}, ckmodels.Event{}, models.User{}, models.Pet{}) + require.NoError(t, err) + requireEqualContent(t, sql, "testdata/mysql_default") + resetSession() + l = New("mysql", WithConfig( &gorm.Config{ DisableForeignKeyConstraintWhenMigrating: true, }, )) - sql, err := l.Load(models.Pet{}, models.User{}) + sql, err = l.Load(ckmodels.Location{}, ckmodels.Event{}) + require.NoError(t, err) + requireEqualContent(t, sql, "testdata/mysql_no_fk") +} + +func resetSession() { + sess, ok := recordriver.Session("gorm") + if ok { + sess.Statements = nil + } +} + +func requireEqualContent(t *testing.T, expected, fileName string) { + buf, err := os.ReadFile(fileName) require.NoError(t, err) - require.Contains(t, sql, "CREATE TABLE `pets`") - require.Contains(t, sql, "CREATE TABLE `users`") - require.NotContains(t, sql, "FOREIGN KEY") + require.Equal(t, expected, string(buf)) } diff --git a/gormschema/testdata/mysql_default b/gormschema/testdata/mysql_default new file mode 100644 index 0000000..b786753 --- /dev/null +++ b/gormschema/testdata/mysql_default @@ -0,0 +1,7 @@ +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 `users` (`id` bigint unsigned AUTO_INCREMENT,`created_at` datetime(3) NULL,`updated_at` datetime(3) NULL,`deleted_at` datetime(3) NULL,`name` longtext,PRIMARY KEY (`id`),INDEX `idx_users_deleted_at` (`deleted_at`)); +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`)); +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 `pets` ADD CONSTRAINT `fk_users_pets` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`); diff --git a/gormschema/testdata/mysql_no_fk b/gormschema/testdata/mysql_no_fk new file mode 100644 index 0000000..d56ea33 --- /dev/null +++ b/gormschema/testdata/mysql_no_fk @@ -0,0 +1,2 @@ +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`)); diff --git a/gormschema/testdata/postgresql_default b/gormschema/testdata/postgresql_default new file mode 100644 index 0000000..634ef99 --- /dev/null +++ b/gormschema/testdata/postgresql_default @@ -0,0 +1,11 @@ +CREATE TABLE "events" ("eventId" text,"locationId" varchar(191),PRIMARY KEY ("eventId")); +CREATE UNIQUE INDEX IF NOT EXISTS "idx_events_location_id" ON "events" ("locationId"); +CREATE TABLE "locations" ("locationId" text,"eventId" varchar(191),PRIMARY KEY ("locationId")); +CREATE UNIQUE INDEX IF NOT EXISTS "idx_locations_event_id" ON "locations" ("eventId"); +CREATE TABLE "users" ("id" bigserial,"created_at" timestamptz,"updated_at" timestamptz,"deleted_at" timestamptz,"name" text,PRIMARY KEY ("id")); +CREATE INDEX IF NOT EXISTS "idx_users_deleted_at" ON "users" ("deleted_at"); +CREATE TABLE "pets" ("id" bigserial,"created_at" timestamptz,"updated_at" timestamptz,"deleted_at" timestamptz,"name" text,"user_id" bigint,PRIMARY KEY ("id")); +CREATE INDEX IF NOT EXISTS "idx_pets_deleted_at" ON "pets" ("deleted_at"); +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 "pets" ADD CONSTRAINT "fk_users_pets" FOREIGN KEY ("user_id") REFERENCES "users"("id"); diff --git a/gormschema/testdata/postgresql_no_fk b/gormschema/testdata/postgresql_no_fk new file mode 100644 index 0000000..ebd90e8 --- /dev/null +++ b/gormschema/testdata/postgresql_no_fk @@ -0,0 +1,4 @@ +CREATE TABLE "events" ("eventId" text,"locationId" varchar(191),PRIMARY KEY ("eventId")); +CREATE UNIQUE INDEX IF NOT EXISTS "idx_events_location_id" ON "events" ("locationId"); +CREATE TABLE "locations" ("locationId" text,"eventId" varchar(191),PRIMARY KEY ("locationId")); +CREATE UNIQUE INDEX IF NOT EXISTS "idx_locations_event_id" ON "locations" ("eventId"); diff --git a/gormschema/testdata/sqlite_default b/gormschema/testdata/sqlite_default new file mode 100644 index 0000000..4911dec --- /dev/null +++ b/gormschema/testdata/sqlite_default @@ -0,0 +1,8 @@ +CREATE TABLE `users` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`name` text,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`),CONSTRAINT `fk_users_pets` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)); +CREATE INDEX `idx_pets_deleted_at` ON `pets`(`deleted_at`); +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`); diff --git a/gormschema/testdata/sqlite_no_fk b/gormschema/testdata/sqlite_no_fk new file mode 100644 index 0000000..81fc61a --- /dev/null +++ b/gormschema/testdata/sqlite_no_fk @@ -0,0 +1,4 @@ +CREATE TABLE `users` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`name` text,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`); diff --git a/internal/testdata/circularfks/atlas.hcl b/internal/testdata/circularfks/atlas.hcl new file mode 100644 index 0000000..ecebac0 --- /dev/null +++ b/internal/testdata/circularfks/atlas.hcl @@ -0,0 +1,36 @@ +variable "dialect" { + type = string +} + +locals { + dev_url = { + mysql = "docker://mysql/8/dev" + postgres = "docker://postgres/15" + sqlite = "sqlite://file::memory:?cache=shared" + }[var.dialect] +} + +data "external_schema" "gorm" { + program = [ + "go", + "run", + "-mod=mod", + "ariga.io/atlas-provider-gorm", + "load", + "--path", ".", + "--dialect", var.dialect, + ] +} + +env "gorm" { + src = data.external_schema.gorm.url + dev = local.dev_url + migration { + dir = "file://migrations/${var.dialect}" + } + format { + migrate { + diff = "{{ sql . \" \" }}" + } + } +} diff --git a/internal/testdata/circularfks/migrations/mysql/20231217181004.sql b/internal/testdata/circularfks/migrations/mysql/20231217181004.sql new file mode 100644 index 0000000..a11f415 --- /dev/null +++ b/internal/testdata/circularfks/migrations/mysql/20231217181004.sql @@ -0,0 +1,18 @@ +-- Create "events" table +CREATE TABLE `events` ( + `eventId` varchar(191) NOT NULL, + `locationId` varchar(191) NULL, + PRIMARY KEY (`eventId`), + UNIQUE INDEX `idx_events_location_id` (`locationId`) +) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +-- Create "locations" table +CREATE TABLE `locations` ( + `locationId` varchar(191) NOT NULL, + `eventId` varchar(191) NULL, + PRIMARY KEY (`locationId`), + UNIQUE INDEX `idx_locations_event_id` (`eventId`) +) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +-- Modify "events" table +ALTER TABLE `events` ADD CONSTRAINT `fk_locations_event` FOREIGN KEY (`locationId`) REFERENCES `locations` (`locationId`) ON UPDATE NO ACTION ON DELETE NO ACTION; +-- Modify "locations" table +ALTER TABLE `locations` ADD CONSTRAINT `fk_events_location` FOREIGN KEY (`eventId`) REFERENCES `events` (`eventId`) ON UPDATE NO ACTION ON DELETE NO ACTION; diff --git a/internal/testdata/circularfks/migrations/mysql/atlas.sum b/internal/testdata/circularfks/migrations/mysql/atlas.sum new file mode 100644 index 0000000..abdc249 --- /dev/null +++ b/internal/testdata/circularfks/migrations/mysql/atlas.sum @@ -0,0 +1,2 @@ +h1:/eDuTTWwc38n5GkA38A1BlSXBzxW2wGyYUwSi36vCn4= +20231217181004.sql h1:XkiJLMZhj3FwEv2QwlAKjLga9LrOhyupMiPMl1LUi/g= diff --git a/internal/testdata/circularfks/migrations/postgres/20231217181014.sql b/internal/testdata/circularfks/migrations/postgres/20231217181014.sql new file mode 100644 index 0000000..c296cf1 --- /dev/null +++ b/internal/testdata/circularfks/migrations/postgres/20231217181014.sql @@ -0,0 +1,22 @@ +-- Create "events" table +CREATE TABLE "public"."events" ( + "eventId" text NOT NULL, + "locationId" character varying(191) NULL, + PRIMARY KEY ("eventId") +); +-- Create index "idx_events_location_id" to table: "events" +CREATE UNIQUE INDEX "idx_events_location_id" ON "public"."events" ("locationId"); +-- Create "locations" table +CREATE TABLE "public"."locations" ( + "locationId" text NOT NULL, + "eventId" character varying(191) NULL, + PRIMARY KEY ("locationId") +); +-- Create index "idx_locations_event_id" to table: "locations" +CREATE UNIQUE INDEX "idx_locations_event_id" ON "public"."locations" ("eventId"); +-- Modify "events" table +ALTER TABLE "public"."events" ADD + CONSTRAINT "fk_locations_event" FOREIGN KEY ("locationId") REFERENCES "public"."locations" ("locationId") ON UPDATE NO ACTION ON DELETE NO ACTION; +-- Modify "locations" table +ALTER TABLE "public"."locations" ADD + CONSTRAINT "fk_events_location" FOREIGN KEY ("eventId") REFERENCES "public"."events" ("eventId") ON UPDATE NO ACTION ON DELETE NO ACTION; diff --git a/internal/testdata/circularfks/migrations/postgres/atlas.sum b/internal/testdata/circularfks/migrations/postgres/atlas.sum new file mode 100644 index 0000000..667ffb8 --- /dev/null +++ b/internal/testdata/circularfks/migrations/postgres/atlas.sum @@ -0,0 +1,2 @@ +h1:3XDCo8bjuE6yz76MYjtZ8OW7DuQ78ew5LcpegpW1dyg= +20231217181014.sql h1:jtRpW/I2EajKDjApxymFfSj8IPAsSYP6XS0AnyQjoCo= diff --git a/internal/testdata/circularfks/migrations/sqlite/20231217181335.sql b/internal/testdata/circularfks/migrations/sqlite/20231217181335.sql new file mode 100644 index 0000000..7ac85e4 --- /dev/null +++ b/internal/testdata/circularfks/migrations/sqlite/20231217181335.sql @@ -0,0 +1,18 @@ +-- Create "events" table +CREATE TABLE `events` ( + `eventId` text NULL, + `locationId` text NULL, + PRIMARY KEY (`eventId`), + CONSTRAINT `fk_locations_event` FOREIGN KEY (`locationId`) REFERENCES `locations` (`locationId`) ON UPDATE NO ACTION ON DELETE NO ACTION +); +-- Create index "idx_events_location_id" to table: "events" +CREATE UNIQUE INDEX `idx_events_location_id` ON `events` (`locationId`); +-- Create "locations" table +CREATE TABLE `locations` ( + `locationId` text NULL, + `eventId` text NULL, + PRIMARY KEY (`locationId`), + CONSTRAINT `fk_events_location` FOREIGN KEY (`eventId`) REFERENCES `events` (`eventId`) ON UPDATE NO ACTION ON DELETE NO ACTION +); +-- Create index "idx_locations_event_id" to table: "locations" +CREATE UNIQUE INDEX `idx_locations_event_id` ON `locations` (`eventId`); diff --git a/internal/testdata/circularfks/migrations/sqlite/atlas.sum b/internal/testdata/circularfks/migrations/sqlite/atlas.sum new file mode 100644 index 0000000..d249fae --- /dev/null +++ b/internal/testdata/circularfks/migrations/sqlite/atlas.sum @@ -0,0 +1,2 @@ +h1:Cv9hXaT3h17Hs7xZdYc9gwyi/Vxb9eNHSk9KdqC3CH4= +20231217181335.sql h1:7IffNd16agoT8asxJkYBda+uKweN9znvsOBgUlhK0+g= diff --git a/internal/testdata/circularfks/models.go b/internal/testdata/circularfks/models.go new file mode 100644 index 0000000..60b7b29 --- /dev/null +++ b/internal/testdata/circularfks/models.go @@ -0,0 +1,13 @@ +package circularfks + +type Location struct { + LocationID string `gorm:"primaryKey;column:locationId;"` + EventID string `gorm:"uniqueIndex;column:eventId;size:191"` + Event *Event `gorm:"foreignKey:locationId;references:locationId;OnUpdate:CASCADE,OnDelete:CASCADE"` +} + +type Event struct { + EventID string `gorm:"primaryKey;column:eventId;"` + LocationID string `gorm:"uniqueIndex;column:locationId;size:191"` + Location *Location `gorm:"foreignKey:eventId;references:eventId;OnUpdate:CASCADE,OnDelete:CASCADE"` +}