mirror of
https://github.com/ent/ent.git
synced 2026-05-24 09:31:56 +03:00
dialect/sql/schema: versioned migrations (#2337)
* dialect/sql/schema: fix go doc * all: update atlas * dialect/sql/schema: diff connected DB and defined schema and write changes to migration file * dialect/sql/schema: use migration directory and formatter directly instead of a planner * all: update atlas * cmd: add command to create a new migration file * entc/gen: generate main.go with migrate diff example * all: regenerate * cmd/internal/base: make linter happy * all: support Go 1.16 in versioned migrations main.go * entc/gen: put versioned migrations behind feature flag * all: regenerate * cmd/ent: driver-prefixed dsn in migrate diff command * cmd/internal/base: remove prefix from migrate import * cmd/internal/base: use cobra.CheckErr * cmd: remove diff command * entc/gen/template/dialect/sql/feature: remove generated main.go * all: rebase on master and go mod tidy * all: regenerate
This commit is contained in:
@@ -12,12 +12,11 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"ariga.io/atlas/sql/migrate"
|
||||
"ariga.io/atlas/sql/schema"
|
||||
"entgo.io/ent/dialect"
|
||||
entsql "entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/schema/field"
|
||||
|
||||
"ariga.io/atlas/sql/migrate"
|
||||
"ariga.io/atlas/sql/schema"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -61,8 +60,8 @@ func WithDiffHook(hooks ...DiffHook) MigrateOption {
|
||||
}
|
||||
}
|
||||
|
||||
// SkipChanges allows skipping/filtering list of changes
|
||||
// returned by the differ before executing migration planning.
|
||||
// WithSkipChanges allows skipping/filtering list of changes
|
||||
// returned by the Differ before executing migration planning.
|
||||
//
|
||||
// SkipChanges(schema.DropTable|schema.DropColumn)
|
||||
//
|
||||
@@ -72,7 +71,7 @@ func WithSkipChanges(skip ChangeKind) MigrateOption {
|
||||
}
|
||||
}
|
||||
|
||||
// A Change of schema.
|
||||
// A ChangeKind denotes the kind of schema change.
|
||||
type ChangeKind uint
|
||||
|
||||
// List of change types.
|
||||
@@ -173,15 +172,15 @@ func filterChanges(skip ChangeKind) DiffHook {
|
||||
type (
|
||||
// Applier is the interface that wraps the Apply method.
|
||||
Applier interface {
|
||||
// Diff creates the given tables in the database.
|
||||
// Apply applies the given migrate.Plan on the database.
|
||||
Apply(context.Context, dialect.ExecQuerier, *migrate.Plan) error
|
||||
}
|
||||
|
||||
// The ApplyFunc type is an adapter to allow the use of ordinary function as Applier.
|
||||
// If f is a function with the appropriate signature, ApplyFunc(f) is a Applier that calls f.
|
||||
// If f is a function with the appropriate signature, ApplyFunc(f) is an Applier that calls f.
|
||||
ApplyFunc func(context.Context, dialect.ExecQuerier, *migrate.Plan) error
|
||||
|
||||
// ApplyHook defines the "migration applying middleware". A function that gets a Applier and returns a Applier.
|
||||
// ApplyHook defines the "migration applying middleware". A function that gets an Applier and returns an Applier.
|
||||
ApplyHook func(Applier) Applier
|
||||
)
|
||||
|
||||
@@ -190,7 +189,7 @@ func (f ApplyFunc) Apply(ctx context.Context, conn dialect.ExecQuerier, plan *mi
|
||||
return f(ctx, conn, plan)
|
||||
}
|
||||
|
||||
// func adds a list of ApplyHook to the schema migration.
|
||||
// WithApplyHook adds a list of ApplyHook to the schema migration.
|
||||
//
|
||||
// schema.WithApplyHook(func(next schema.Applier) schema.Applier {
|
||||
// return schema.ApplyFunc(func(ctx context.Context, conn dialect.ExecQuerier, plan *migrate.Plan) error {
|
||||
@@ -221,6 +220,20 @@ func WithAtlas(b bool) MigrateOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithDir sets the atlas migration directory to use to store migration files.
|
||||
func WithDir(dir migrate.Dir) MigrateOption {
|
||||
return func(m *Migrate) {
|
||||
m.atlas.dir = dir
|
||||
}
|
||||
}
|
||||
|
||||
// WithFormatter sets atlas formatter to use to write changes to migration files.
|
||||
func WithFormatter(fmt migrate.Formatter) MigrateOption {
|
||||
return func(m *Migrate) {
|
||||
m.atlas.fmt = fmt
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
// atlasOptions describes the options for atlas.
|
||||
atlasOptions struct {
|
||||
@@ -228,6 +241,8 @@ type (
|
||||
diff []DiffHook
|
||||
apply []ApplyHook
|
||||
skip ChangeKind
|
||||
dir migrate.Dir
|
||||
fmt migrate.Formatter
|
||||
}
|
||||
|
||||
// atBuilder must be implemented by the different drivers in
|
||||
@@ -245,7 +260,7 @@ type (
|
||||
|
||||
func (m *Migrate) setupAtlas() error {
|
||||
// Using one of the Atlas options, opt-in to Atlas migration.
|
||||
if !m.atlas.enabled && (m.atlas.skip != NoChange || len(m.atlas.diff) > 0 || len(m.atlas.apply) > 0) {
|
||||
if !m.atlas.enabled && (m.atlas.skip != NoChange || len(m.atlas.diff) > 0 || len(m.atlas.apply) > 0) || m.atlas.dir != nil {
|
||||
m.atlas.enabled = true
|
||||
}
|
||||
if !m.atlas.enabled {
|
||||
@@ -270,6 +285,9 @@ func (m *Migrate) setupAtlas() error {
|
||||
if k == NoChange {
|
||||
m.atlas.diff = append(m.atlas.diff, filterChanges(k))
|
||||
}
|
||||
if m.atlas.dir != nil && m.atlas.fmt == nil {
|
||||
m.atlas.fmt = migrate.DefaultFormatter
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -289,36 +307,7 @@ func (m *Migrate) atCreate(ctx context.Context, tables ...*Table) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
drv, err := m.atOpen(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
current, err := drv.InspectSchema(ctx, "", &schema.InspectOptions{
|
||||
Tables: func() (t []string) {
|
||||
for i := range tables {
|
||||
t = append(t, tables[i].Name)
|
||||
}
|
||||
return t
|
||||
}(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tt, err := m.aTables(ctx, m, tx, tables)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Diff changes.
|
||||
var differ Differ = DiffFunc(drv.SchemaDiff)
|
||||
for i := len(m.atlas.diff) - 1; i >= 0; i-- {
|
||||
differ = m.atlas.diff[i](differ)
|
||||
}
|
||||
changes, err := differ.Diff(current, &schema.Schema{Name: current.Name, Attrs: current.Attrs, Tables: tt})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Plan changes.
|
||||
plan, err := drv.PlanChanges(ctx, "plan", changes)
|
||||
plan, err := m.atDiff(ctx, tx, tables...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -344,6 +333,39 @@ func (m *Migrate) atCreate(ctx context.Context, tables ...*Table) error {
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (m *Migrate) atDiff(ctx context.Context, conn dialect.ExecQuerier, tables ...*Table) (*migrate.Plan, error) {
|
||||
drv, err := m.atOpen(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current, err := drv.InspectSchema(ctx, "", &schema.InspectOptions{
|
||||
Tables: func() (t []string) {
|
||||
for i := range tables {
|
||||
t = append(t, tables[i].Name)
|
||||
}
|
||||
return t
|
||||
}(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tt, err := m.aTables(ctx, m, conn, tables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Diff changes.
|
||||
var differ Differ = DiffFunc(drv.SchemaDiff)
|
||||
for i := len(m.atlas.diff) - 1; i >= 0; i-- {
|
||||
differ = m.atlas.diff[i](differ)
|
||||
}
|
||||
changes, err := differ.Diff(current, &schema.Schema{Name: current.Name, Attrs: current.Attrs, Tables: tt})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Plan changes.
|
||||
return drv.PlanChanges(ctx, "", changes)
|
||||
}
|
||||
|
||||
type db struct{ dialect.ExecQuerier }
|
||||
|
||||
func (d *db) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
|
||||
|
||||
@@ -7,9 +7,11 @@ package schema
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"ariga.io/atlas/sql/migrate"
|
||||
"entgo.io/ent/dialect"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/schema/field"
|
||||
@@ -137,7 +139,7 @@ func NewMigrate(d dialect.Driver, opts ...MigrateOption) (*Migrate, error) {
|
||||
}
|
||||
|
||||
// Create creates all schema resources in the database. It works in an "append-only"
|
||||
// mode, which means, it only create tables, append column to tables or modifying column type.
|
||||
// mode, which means, it only creates tables, appends columns to tables or modifies column types.
|
||||
//
|
||||
// Column can be modified by turning into a NULL from NOT NULL, or having a type conversion not
|
||||
// resulting data altering. From example, changing varchar(255) to varchar(120) is invalid, but
|
||||
@@ -159,6 +161,19 @@ func (m *Migrate) Create(ctx context.Context, tables ...*Table) error {
|
||||
return creator.Create(ctx, tables...)
|
||||
}
|
||||
|
||||
// Diff compares the state read from the StateReader with the state defined by Ent.
|
||||
// Changes will be written to migration files by the configures Planner.
|
||||
func (m *Migrate) Diff(ctx context.Context, tables ...*Table) error {
|
||||
if m.atlas.dir == nil {
|
||||
return errors.New("no migration directory given")
|
||||
}
|
||||
plan, err := m.atDiff(ctx, m, tables...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return migrate.New(nil, m.atlas.dir, m.atlas.fmt).WritePlan(plan)
|
||||
}
|
||||
|
||||
func (m *Migrate) create(ctx context.Context, tables ...*Table) error {
|
||||
tx, err := m.Tx(ctx)
|
||||
if err != nil {
|
||||
@@ -383,7 +398,6 @@ func (m *Migrate) changeSet(curr, new *Table) (*changes, error) {
|
||||
change.column.modify = append(change.column.modify, c1)
|
||||
}
|
||||
}
|
||||
|
||||
// Drop columns.
|
||||
for _, c1 := range curr.Columns {
|
||||
// If a column was dropped, multi-columns indexes that are associated with this column will
|
||||
@@ -394,7 +408,6 @@ func (m *Migrate) changeSet(curr, new *Table) (*changes, error) {
|
||||
change.column.drop = append(change.column.drop, c1)
|
||||
}
|
||||
}
|
||||
|
||||
// Add or modify indexes.
|
||||
for _, idx1 := range new.Indexes {
|
||||
switch idx2, ok := curr.index(idx1.Name); {
|
||||
@@ -413,7 +426,6 @@ func (m *Migrate) changeSet(curr, new *Table) (*changes, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Drop indexes.
|
||||
for _, idx := range curr.Indexes {
|
||||
if _, isFK := new.fk(idx.Name); !isFK && !new.hasIndex(idx.Name, idx.realname) {
|
||||
|
||||
Reference in New Issue
Block a user