dialect/sql/schema: use withForeighKeys() instead of withForeighKeysDisable(), and add unit tests () (#748)

This commit is contained in:
day-dreams
2020-09-20 16:49:47 +08:00
parent 54028de04f
commit 4582ed0f2a
59 changed files with 239 additions and 9641 deletions

View File

@@ -59,28 +59,28 @@ func WithFixture(b bool) MigrateOption {
}
}
// WithForeighKeysDisable disables creating foreigh-key in ddl. In some case we are
// not allowed to use foreigh-key. Defaults to false
func WithForeighKeysDisable(b bool) MigrateOption {
// WithForeighKeys enables creating foreigh-key in ddl. In some case we are
// not allowed to use foreigh-key. Defaults to true
func WithForeighKeys(b bool) MigrateOption {
return func(m *Migrate) {
m.disableForeighKeys = b
m.withForeighKeys = 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.
disableForeighKeys bool // do not create foreigh keys
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.
withForeighKeys bool // with foreigh 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{withFixture: true}
m := &Migrate{withFixture: true, withForeighKeys: true}
switch d.Dialect() {
case dialect.MySQL:
m.sqlDialect = &MySQL{Driver: d}
@@ -128,9 +128,6 @@ func (m *Migrate) Create(ctx context.Context, tables ...*Table) error {
func (m *Migrate) create(ctx context.Context, tx dialect.Tx, tables ...*Table) error {
for _, t := range tables {
m.setupTable(t)
if m.disableForeighKeys {
t.ForeignKeys = nil
}
switch exist, err := m.tableExist(ctx, tx, t.Name); {
case err != nil:
return err
@@ -153,7 +150,15 @@ func (m *Migrate) create(ctx context.Context, tx dialect.Tx, tables ...*Table) e
return err
}
default: // !exist
// special case: sqllite driver create foreigh keys during table creating period,
// we need to set foreigh keys as emtpy before it, and get FKS back after Query().
bks := t.ForeignKeys
if _, ok := m.sqlDialect.(*SQLite); ok && !m.withForeighKeys {
t.ForeignKeys = nil
}
query, args := m.tBuilder(t).Query()
t.ForeignKeys = bks
// create table
if err := tx.Exec(ctx, query, args, nil); err != nil {
return fmt.Errorf("create table %q: %v", t.Name, err)
}
@@ -176,7 +181,7 @@ func (m *Migrate) create(ctx context.Context, tx dialect.Tx, tables ...*Table) e
// Create foreign keys after tables were created/altered,
// because circular foreign-key constraints are possible.
for _, t := range tables {
if len(t.ForeignKeys) == 0 {
if !m.withForeighKeys || len(t.ForeignKeys) == 0 {
continue
}
fks := make([]*ForeignKey, 0, len(t.ForeignKeys))
@@ -357,7 +362,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.withForeighKeys || !ok {
return nil
}
rename := make(map[string]*Index)

View File

@@ -143,6 +143,56 @@ func TestMySQL_Create(t *testing.T) {
mock.ExpectCommit()
},
},
{
name: "create new table with foreign key diabled",
options: []MigrateOption{
WithForeighKeys(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{

View File

@@ -125,6 +125,56 @@ func TestPostgres_Create(t *testing.T) {
mock.ExpectCommit()
},
},
{
name: "create new table with foreign key disabled",
options: []MigrateOption{
WithForeighKeys(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{

View File

@@ -123,6 +123,56 @@ func TestSQLite_Create(t *testing.T) {
mock.ExpectCommit()
},
},
{
name: "create new table with foreign key disabled",
options: []MigrateOption{
WithForeighKeys(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{