mirror of
https://github.com/ent/ent.git
synced 2026-05-22 09:31:45 +03:00
dialect/sql/schema: add schema dump command (#4296)
* dialect/sql/schema: add schema dump command * entc/gen/template: drop build flag from generated globalid.go
This commit is contained in:
@@ -525,6 +525,7 @@ func (a *Atlas) StateReader(tables ...*Table) migrate.StateReaderFunc {
|
||||
}
|
||||
a.sqlDialect = drv
|
||||
}
|
||||
a.setupTables(tables)
|
||||
ts, err := a.tables(tables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -6,10 +6,18 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"ariga.io/atlas/sql/migrate"
|
||||
"ariga.io/atlas/sql/mysql"
|
||||
"ariga.io/atlas/sql/postgres"
|
||||
"ariga.io/atlas/sql/schema"
|
||||
"ariga.io/atlas/sql/sqlite"
|
||||
entdialect "entgo.io/ent/dialect"
|
||||
"entgo.io/ent/dialect/entsql"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/schema/field"
|
||||
@@ -576,3 +584,63 @@ func indexType(idx *Index, d string) (string, bool) {
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
type driver struct {
|
||||
sqlDialect
|
||||
schema.Differ
|
||||
migrate.PlanApplier
|
||||
}
|
||||
|
||||
var drivers = func(v string) map[string]driver {
|
||||
return map[string]driver{
|
||||
entdialect.SQLite: {
|
||||
&SQLite{WithForeignKeys: true},
|
||||
sqlite.DefaultDiff,
|
||||
sqlite.DefaultPlan,
|
||||
},
|
||||
entdialect.MySQL: {
|
||||
&MySQL{version: v},
|
||||
mysql.DefaultDiff,
|
||||
mysql.DefaultPlan,
|
||||
},
|
||||
entdialect.Postgres: {
|
||||
&Postgres{version: v},
|
||||
postgres.DefaultDiff,
|
||||
postgres.DefaultPlan,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Dump the schema DDL for the given tables.
|
||||
func Dump(ctx context.Context, dialect, version string, tables []*Table, opts ...migrate.PlanOption) (string, error) {
|
||||
opts = append([]migrate.PlanOption{func(o *migrate.PlanOptions) {
|
||||
o.Mode = migrate.PlanModeDump
|
||||
o.Indent = " "
|
||||
}}, opts...)
|
||||
d, ok := drivers(version)[dialect]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unsupported dialect %q", dialect)
|
||||
}
|
||||
r, err := (&Atlas{sqlDialect: d}).StateReader(tables...).ReadState(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var c schema.Changes
|
||||
if slices.ContainsFunc(tables, func(t *Table) bool { return t.Schema != "" }) {
|
||||
c, err = d.RealmDiff(&schema.Realm{}, r)
|
||||
} else {
|
||||
c, err = d.SchemaDiff(&schema.Schema{}, r.Schemas[0])
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
p, err := d.PlanChanges(ctx, "dump", c, opts...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
f, err := migrate.DefaultFormatter.FormatFile(p)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(f.Bytes()), nil
|
||||
}
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"ariga.io/atlas/sql/migrate"
|
||||
"entgo.io/ent/dialect"
|
||||
"entgo.io/ent/dialect/entsql"
|
||||
"entgo.io/ent/schema/field"
|
||||
|
||||
@@ -149,3 +153,76 @@ func TestCopyTables(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tables, copyT)
|
||||
}
|
||||
|
||||
func TestDump(t *testing.T) {
|
||||
users := &Table{
|
||||
Name: "users",
|
||||
Columns: []*Column{
|
||||
{Name: "id", Type: field.TypeInt},
|
||||
{Name: "name", Type: field.TypeString},
|
||||
{Name: "spouse_id", Type: field.TypeInt},
|
||||
},
|
||||
}
|
||||
users.PrimaryKey = users.Columns[:1]
|
||||
users.Indexes = append(users.Indexes, &Index{
|
||||
Name: "name",
|
||||
Columns: users.Columns[1:2],
|
||||
})
|
||||
users.AddForeignKey(&ForeignKey{
|
||||
Columns: users.Columns[2:],
|
||||
RefTable: users,
|
||||
RefColumns: users.Columns[:1],
|
||||
OnUpdate: SetDefault,
|
||||
})
|
||||
users.SetAnnotation(&entsql.Annotation{Table: "Users"})
|
||||
pets := &Table{
|
||||
Name: "pets",
|
||||
Columns: []*Column{
|
||||
{Name: "id", Type: field.TypeInt},
|
||||
{Name: "name", Type: field.TypeString},
|
||||
{Name: "fur_color", Type: field.TypeEnum, Enums: []string{"black", "white"}},
|
||||
{Name: "owner_id", Type: field.TypeInt},
|
||||
},
|
||||
}
|
||||
pets.Indexes = append(pets.Indexes, &Index{
|
||||
Name: "name",
|
||||
Unique: true,
|
||||
Columns: pets.Columns[1:2],
|
||||
Annotation: entsql.Desc(),
|
||||
})
|
||||
pets.AddForeignKey(&ForeignKey{
|
||||
Columns: pets.Columns[3:],
|
||||
RefTable: users,
|
||||
RefColumns: users.Columns[:1],
|
||||
OnDelete: SetDefault,
|
||||
})
|
||||
tables = []*Table{users, pets}
|
||||
|
||||
my := func(length int) string {
|
||||
return fmt.Sprintf("-- Create \"users\" table\nCREATE TABLE `users` (`id` bigint NOT NULL, `name` varchar(%d) NOT NULL, `spouse_id` bigint NOT NULL, PRIMARY KEY (`id`), INDEX `name` (`name`), FOREIGN KEY (`spouse_id`) REFERENCES `users` (`id`) ON UPDATE SET DEFAULT) CHARSET utf8mb4 COLLATE utf8mb4_bin;\n-- Create \"pets\" table\nCREATE TABLE `pets` (`id` bigint NOT NULL, `name` varchar(%d) NOT NULL, `fur_color` enum('black','white') NOT NULL, `owner_id` bigint NOT NULL, UNIQUE INDEX `name` (`name` DESC), FOREIGN KEY (`owner_id`) REFERENCES `users` (`id`) ON DELETE SET DEFAULT) CHARSET utf8mb4 COLLATE utf8mb4_bin;\n", length, length)
|
||||
}
|
||||
|
||||
pg := "-- Create \"users\" table\nCREATE TABLE \"users\" (\"id\" bigint NOT NULL, \"name\" character varying NOT NULL, \"spouse_id\" bigint NOT NULL, PRIMARY KEY (\"id\"), FOREIGN KEY (\"spouse_id\") REFERENCES \"users\" (\"id\") ON UPDATE SET DEFAULT);\n-- Create index \"name\" to table: \"users\"\nCREATE INDEX \"name\" ON \"users\" (\"name\");\n-- Create \"pets\" table\nCREATE TABLE \"pets\" (\"id\" bigint NOT NULL, \"name\" character varying NOT NULL, \"fur_color\" character varying NOT NULL, \"owner_id\" bigint NOT NULL, FOREIGN KEY (\"owner_id\") REFERENCES \"users\" (\"id\") ON DELETE SET DEFAULT);\n-- Create index \"name\" to table: \"pets\"\nCREATE UNIQUE INDEX \"name\" ON \"pets\" (\"name\" DESC);\n"
|
||||
|
||||
for _, tt := range []struct{ dialect, version, expected string }{
|
||||
{
|
||||
dialect.SQLite, "",
|
||||
"-- Create \"users\" table\nCREATE TABLE `users` (`id` integer NOT NULL, `name` text NOT NULL, `spouse_id` integer NOT NULL, PRIMARY KEY (`id`), FOREIGN KEY (`spouse_id`) REFERENCES `users` (`id`) ON UPDATE SET DEFAULT);\n-- Create index \"name\" to table: \"users\"\nCREATE INDEX `name` ON `users` (`name`);\n-- Create \"pets\" table\nCREATE TABLE `pets` (`id` integer NOT NULL, `name` text NOT NULL, `fur_color` text NOT NULL, `owner_id` integer NOT NULL, FOREIGN KEY (`owner_id`) REFERENCES `users` (`id`) ON DELETE SET DEFAULT);\n-- Create index \"name\" to table: \"pets\"\nCREATE UNIQUE INDEX `name` ON `pets` (`name` DESC);\n",
|
||||
},
|
||||
{dialect.MySQL, "5.6", my(191)},
|
||||
{dialect.MySQL, "5.7", my(255)},
|
||||
{dialect.MySQL, "8", my(255)},
|
||||
{dialect.Postgres, "12", pg},
|
||||
{dialect.Postgres, "13", pg},
|
||||
{dialect.Postgres, "14", pg},
|
||||
{dialect.Postgres, "15", pg},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("%s:%s", tt.dialect, tt.version), func(t *testing.T) {
|
||||
ac, err := Dump(context.Background(), tt.dialect, tt.version, tables, func(o *migrate.PlanOptions) {
|
||||
o.Indent = ""
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.expected, ac)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user