mirror of
https://github.com/ent/ent.git
synced 2026-05-22 09:31:45 +03:00
Merge branch 'master' of https://github.com/day-dreams/ent into day-dreams-master
This commit is contained in:
@@ -59,32 +59,40 @@ func WithFixture(b bool) MigrateOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithForeignKeys enables creating foreign-key in ddl. Defaults to true.
|
||||
func WithForeignKeys(b bool) MigrateOption {
|
||||
return func(m *Migrate) {
|
||||
m.withForeignKeys = b
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate runs the migrations logic for the SQL dialects.
|
||||
type Migrate struct {
|
||||
sqlDialect
|
||||
universalID bool // global unique ids.
|
||||
dropColumns bool // drop deleted columns.
|
||||
dropIndexes bool // drop deleted indexes.
|
||||
withFixture bool // with fks rename fixture.
|
||||
typeRanges []string // types order by their range.
|
||||
universalID bool // global unique ids.
|
||||
dropColumns bool // drop deleted columns.
|
||||
dropIndexes bool // drop deleted indexes.
|
||||
withFixture bool // with fks rename fixture.
|
||||
withForeignKeys bool // with foreign keys
|
||||
typeRanges []string // types order by their range.
|
||||
}
|
||||
|
||||
// NewMigrate create a migration structure for the given SQL driver.
|
||||
func NewMigrate(d dialect.Driver, opts ...MigrateOption) (*Migrate, error) {
|
||||
m := &Migrate{}
|
||||
m := &Migrate{withForeignKeys: true}
|
||||
for _, opt := range opts {
|
||||
opt(m)
|
||||
}
|
||||
switch d.Dialect() {
|
||||
case dialect.MySQL:
|
||||
m.sqlDialect = &MySQL{Driver: d}
|
||||
case dialect.SQLite:
|
||||
m.sqlDialect = &SQLite{Driver: d}
|
||||
m.sqlDialect = &SQLite{Driver: d, WithForeignKeys: m.withForeignKeys}
|
||||
case dialect.Postgres:
|
||||
m.sqlDialect = &Postgres{Driver: d}
|
||||
default:
|
||||
return nil, fmt.Errorf("sql/schema: unsupported dialect %q", d.Dialect())
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(m)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@@ -161,6 +169,9 @@ func (m *Migrate) create(ctx context.Context, tx dialect.Tx, tables ...*Table) e
|
||||
}
|
||||
}
|
||||
}
|
||||
if !m.withForeignKeys {
|
||||
return nil
|
||||
}
|
||||
// Create foreign keys after tables were created/altered,
|
||||
// because circular foreign-key constraints are possible.
|
||||
for _, t := range tables {
|
||||
@@ -345,7 +356,7 @@ func (m *Migrate) changeSet(curr, new *Table) (*changes, error) {
|
||||
// fixture is a special migration code for renaming foreign-key columns (issue-#285).
|
||||
func (m *Migrate) fixture(ctx context.Context, tx dialect.Tx, curr, new *Table) error {
|
||||
d, ok := m.sqlDialect.(fkRenamer)
|
||||
if !m.withFixture || !ok {
|
||||
if !m.withFixture || !m.withForeignKeys || !ok {
|
||||
return nil
|
||||
}
|
||||
rename := make(map[string]*Index)
|
||||
|
||||
@@ -143,6 +143,56 @@ func TestMySQL_Create(t *testing.T) {
|
||||
mock.ExpectCommit()
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create new table with foreign key diabled",
|
||||
options: []MigrateOption{
|
||||
WithForeignKeys(false),
|
||||
},
|
||||
tables: func() []*Table {
|
||||
var (
|
||||
c1 = []*Column{
|
||||
{Name: "id", Type: field.TypeInt, Increment: true},
|
||||
{Name: "name", Type: field.TypeString, Nullable: true},
|
||||
{Name: "created_at", Type: field.TypeTime},
|
||||
}
|
||||
c2 = []*Column{
|
||||
{Name: "id", Type: field.TypeInt, Increment: true},
|
||||
{Name: "name", Type: field.TypeString},
|
||||
{Name: "owner_id", Type: field.TypeInt, Nullable: true},
|
||||
}
|
||||
t1 = &Table{
|
||||
Name: "users",
|
||||
Columns: c1,
|
||||
PrimaryKey: c1[0:1],
|
||||
}
|
||||
t2 = &Table{
|
||||
Name: "pets",
|
||||
Columns: c2,
|
||||
PrimaryKey: c2[0:1],
|
||||
ForeignKeys: []*ForeignKey{
|
||||
{
|
||||
Symbol: "pets_owner",
|
||||
Columns: c2[2:],
|
||||
RefTable: t1,
|
||||
RefColumns: c1[0:1],
|
||||
OnDelete: Cascade,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
return []*Table{t1, t2}
|
||||
}(),
|
||||
before: func(mock mysqlMock) {
|
||||
mock.start("5.7.23")
|
||||
mock.tableExists("users", false)
|
||||
mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `users`(`id` bigint AUTO_INCREMENT NOT NULL, `name` varchar(255) NULL, `created_at` timestamp NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
mock.tableExists("pets", false)
|
||||
mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `pets`(`id` bigint AUTO_INCREMENT NOT NULL, `name` varchar(255) NOT NULL, `owner_id` bigint NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
mock.ExpectCommit()
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add column to table",
|
||||
tables: []*Table{
|
||||
|
||||
@@ -125,6 +125,56 @@ func TestPostgres_Create(t *testing.T) {
|
||||
mock.ExpectCommit()
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create new table with foreign key disabled",
|
||||
options: []MigrateOption{
|
||||
WithForeignKeys(false),
|
||||
},
|
||||
tables: func() []*Table {
|
||||
var (
|
||||
c1 = []*Column{
|
||||
{Name: "id", Type: field.TypeInt, Increment: true},
|
||||
{Name: "name", Type: field.TypeString, Nullable: true},
|
||||
{Name: "created_at", Type: field.TypeTime},
|
||||
}
|
||||
c2 = []*Column{
|
||||
{Name: "id", Type: field.TypeInt, Increment: true},
|
||||
{Name: "name", Type: field.TypeString},
|
||||
{Name: "owner_id", Type: field.TypeInt, Nullable: true},
|
||||
}
|
||||
t1 = &Table{
|
||||
Name: "users",
|
||||
Columns: c1,
|
||||
PrimaryKey: c1[0:1],
|
||||
}
|
||||
t2 = &Table{
|
||||
Name: "pets",
|
||||
Columns: c2,
|
||||
PrimaryKey: c2[0:1],
|
||||
ForeignKeys: []*ForeignKey{
|
||||
{
|
||||
Symbol: "pets_owner",
|
||||
Columns: c2[2:],
|
||||
RefTable: t1,
|
||||
RefColumns: c1[0:1],
|
||||
OnDelete: Cascade,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
return []*Table{t1, t2}
|
||||
}(),
|
||||
before: func(mock pgMock) {
|
||||
mock.start("120000")
|
||||
mock.tableExists("users", false)
|
||||
mock.ExpectExec(escape(`CREATE TABLE IF NOT EXISTS "users"("id" bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL, "name" varchar NULL, "created_at" timestamp with time zone NOT NULL, PRIMARY KEY("id"))`)).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
mock.tableExists("pets", false)
|
||||
mock.ExpectExec(escape(`CREATE TABLE IF NOT EXISTS "pets"("id" bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL, "name" varchar NOT NULL, "owner_id" bigint NULL, PRIMARY KEY("id"))`)).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
mock.ExpectCommit()
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add column to table",
|
||||
tables: []*Table{
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
// SQLite is an SQLite migration driver.
|
||||
type SQLite struct {
|
||||
dialect.Driver
|
||||
WithForeignKeys bool
|
||||
}
|
||||
|
||||
// init makes sure that foreign_keys support is enabled.
|
||||
@@ -76,8 +77,10 @@ func (d *SQLite) tBuilder(t *Table) *sql.TableBuilder {
|
||||
// not always valid (because circular foreign-keys situation is possible).
|
||||
// We stay consistent by not using constraints at all, and just defining the
|
||||
// foreign keys in the `CREATE TABLE` statement.
|
||||
for _, fk := range t.ForeignKeys {
|
||||
b.ForeignKeys(fk.DSL())
|
||||
if d.WithForeignKeys {
|
||||
for _, fk := range t.ForeignKeys {
|
||||
b.ForeignKeys(fk.DSL())
|
||||
}
|
||||
}
|
||||
// If it's an ID based primary key with autoincrement, we add
|
||||
// the `PRIMARY KEY` clause to the column declaration. Otherwise,
|
||||
|
||||
@@ -123,6 +123,56 @@ func TestSQLite_Create(t *testing.T) {
|
||||
mock.ExpectCommit()
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create new table with foreign key disabled",
|
||||
options: []MigrateOption{
|
||||
WithForeignKeys(false),
|
||||
},
|
||||
tables: func() []*Table {
|
||||
var (
|
||||
c1 = []*Column{
|
||||
{Name: "id", Type: field.TypeInt, Increment: true},
|
||||
{Name: "name", Type: field.TypeString, Nullable: true},
|
||||
{Name: "created_at", Type: field.TypeTime},
|
||||
}
|
||||
c2 = []*Column{
|
||||
{Name: "id", Type: field.TypeInt, Increment: true},
|
||||
{Name: "name", Type: field.TypeString},
|
||||
{Name: "owner_id", Type: field.TypeInt, Nullable: true},
|
||||
}
|
||||
t1 = &Table{
|
||||
Name: "users",
|
||||
Columns: c1,
|
||||
PrimaryKey: c1[0:1],
|
||||
}
|
||||
t2 = &Table{
|
||||
Name: "pets",
|
||||
Columns: c2,
|
||||
PrimaryKey: c2[0:1],
|
||||
ForeignKeys: []*ForeignKey{
|
||||
{
|
||||
Symbol: "pets_owner",
|
||||
Columns: c2[2:],
|
||||
RefTable: t1,
|
||||
RefColumns: c1[0:1],
|
||||
OnDelete: Cascade,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
return []*Table{t1, t2}
|
||||
}(),
|
||||
before: func(mock sqliteMock) {
|
||||
mock.start()
|
||||
mock.tableExists("users", false)
|
||||
mock.ExpectExec(escape("CREATE TABLE `users`(`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `name` varchar(255) NULL, `created_at` datetime NOT NULL)")).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
mock.tableExists("pets", false)
|
||||
mock.ExpectExec(escape("CREATE TABLE `pets`(`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `name` varchar(255) NOT NULL, `owner_id` integer NULL)")).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
mock.ExpectCommit()
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add column to table",
|
||||
tables: []*Table{
|
||||
|
||||
Reference in New Issue
Block a user