Skip to content

Commit

Permalink
ouput correct ddl for custom m2m tables (#35)
Browse files Browse the repository at this point in the history
* ouput correct ddl for custom m2m tables

* fix tests

* fix typo

* cr
  • Loading branch information
ronenlu authored Mar 20, 2024
1 parent f1471a5 commit cb8b534
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 8 deletions.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,39 @@ The provider supports the following databases:
* SQLite
* SQL Server

### Frequently Asked Questions

* **Foreign key constraints not generated correctly** -
If a [Customize JoinTable](https://gorm.io/docs/many_to_many.html#Customize-JoinTable) is defined in the schema,
you need to use the provider as a [Go Program](#as-go-file) and pass to the `Load` method the tables in their dependency order. i.e.,
Join tables after their parent tables.

for example if those are your models:
```go
type Person struct {
ID int
Name string
Addresses []Address `gorm:"many2many:person_addresses;"`
}

type Address struct {
ID int
Name string
}

type PersonAddress struct {
PersonID int `gorm:"primaryKey"`
AddressID int `gorm:"primaryKey"`
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}
```

you should use the following code:
```go
stmts, err := gormschema.New("mysql").Load(&models.Person{}, &models.Address{}, &models.PersonAddress{})
```

### License

This project is licensed under the [Apache License 2.0](LICENSE).
7 changes: 5 additions & 2 deletions gormschema/gorm.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"database/sql/driver"
"errors"
"fmt"
"slices"

"ariga.io/atlas-go-sdk/recordriver"
"gorm.io/driver/mysql"
Expand Down Expand Up @@ -133,12 +134,14 @@ func (d dialector) Migrator(db *gorm.DB) gorm.Migrator {
}

// 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 {
func (m *migrator) HasTable(dst any) bool {
return true
}

// CreateConstraints detects constraints on the given model and creates them using `m.dialectMigrator`.
func (m *migrator) CreateConstraints(models []interface{}) error {
func (m *migrator) CreateConstraints(models []any) error {
// Reverse the order of models to ensure many 2 many tables constraints are created first, assuming they are at the end.
slices.Reverse(models)
for _, model := range m.ReorderModels(models, true) {
err := m.Migrator.RunWithValue(model, func(stmt *gorm.Statement) error {
for _, rel := range stmt.Schema.Relationships.Relations {
Expand Down
6 changes: 6 additions & 0 deletions gormschema/gorm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"ariga.io/atlas-go-sdk/recordriver"
ckmodels "ariga.io/atlas-provider-gorm/internal/testdata/circularfks"
"ariga.io/atlas-provider-gorm/internal/testdata/customjointable"
"ariga.io/atlas-provider-gorm/internal/testdata/models"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
Expand Down Expand Up @@ -58,6 +59,11 @@ func TestMySQLConfig(t *testing.T) {
sql, err = l.Load(ckmodels.Location{}, ckmodels.Event{})
require.NoError(t, err)
requireEqualContent(t, sql, "testdata/mysql_no_fk")
resetSession()
l = New("mysql")
sql, err = l.Load(customjointable.Address{}, customjointable.Person{}, customjointable.PersonAddress{})
require.NoError(t, err)
requireEqualContent(t, sql, "testdata/mysql_custom_join_table")
}

func TestSQLServerConfig(t *testing.T) {
Expand Down
5 changes: 5 additions & 0 deletions gormschema/testdata/mysql_custom_join_table
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE `addresses` (`id` bigint AUTO_INCREMENT,`name` longtext,PRIMARY KEY (`id`));
CREATE TABLE `people` (`id` bigint AUTO_INCREMENT,`name` longtext,PRIMARY KEY (`id`));
CREATE TABLE `person_addresses` (`person_id` bigint,`address_id` bigint,`created_at` datetime(3) NULL,`deleted_at` datetime(3) NULL,PRIMARY KEY (`person_id`,`address_id`));
ALTER TABLE `person_addresses` ADD CONSTRAINT `fk_person_addresses_person` FOREIGN KEY (`person_id`) REFERENCES `people`(`id`);
ALTER TABLE `person_addresses` ADD CONSTRAINT `fk_person_addresses_address` FOREIGN KEY (`address_id`) REFERENCES `addresses`(`id`);
4 changes: 2 additions & 2 deletions gormschema/testdata/mysql_default
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ CREATE TABLE `events` (`eventId` varchar(191),`locationId` varchar(191),PRIMARY
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`);
ALTER TABLE `locations` ADD CONSTRAINT `fk_events_location` FOREIGN KEY (`eventId`) REFERENCES `events`(`eventId`);
ALTER TABLE `events` ADD CONSTRAINT `fk_locations_event` FOREIGN KEY (`locationId`) REFERENCES `locations`(`locationId`);
4 changes: 2 additions & 2 deletions gormschema/testdata/postgresql_default
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ CREATE TABLE "users" ("id" bigserial,"created_at" timestamptz,"updated_at" times
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");
ALTER TABLE "locations" ADD CONSTRAINT "fk_events_location" FOREIGN KEY ("eventId") REFERENCES "events"("eventId");
ALTER TABLE "events" ADD CONSTRAINT "fk_locations_event" FOREIGN KEY ("locationId") REFERENCES "locations"("locationId");
4 changes: 2 additions & 2 deletions gormschema/testdata/sqlserver_default
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ CREATE TABLE "users" ("id" bigint IDENTITY(1,1),"created_at" datetimeoffset,"upd
CREATE INDEX "idx_users_deleted_at" ON "users"("deleted_at");
CREATE TABLE "pets" ("id" bigint IDENTITY(1,1),"created_at" datetimeoffset,"updated_at" datetimeoffset,"deleted_at" datetimeoffset,"name" nvarchar(MAX),"user_id" bigint,PRIMARY KEY ("id"));
CREATE INDEX "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");
ALTER TABLE "locations" ADD CONSTRAINT "fk_events_location" FOREIGN KEY ("eventId") REFERENCES "events"("eventId");
ALTER TABLE "events" ADD CONSTRAINT "fk_locations_event" FOREIGN KEY ("locationId") REFERENCES "locations"("locationId");
26 changes: 26 additions & 0 deletions internal/testdata/customjointable/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package customjointable


import (
"time"

"gorm.io/gorm"
)

type Person struct {
ID int
Name string
Addresses []Address `gorm:"many2many:person_addresses;"`
}

type Address struct {
ID int
Name string
}

type PersonAddress struct {
PersonID int `gorm:"primaryKey"`
AddressID int `gorm:"primaryKey"`
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}

0 comments on commit cb8b534

Please sign in to comment.