|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/go-sql-driver/mysql"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
|
|
|
|
"github.com/zeromicro/go-zero/core/collection"
|
|
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|
|
|
"github.com/zeromicro/go-zero/core/stores/postgres"
|
|
|
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
|
|
"github.com/zeromicro/go-zero/tools/goctl/config"
|
|
|
|
"github.com/zeromicro/go-zero/tools/goctl/model/sql/command/migrationnotes"
|
|
|
|
"github.com/zeromicro/go-zero/tools/goctl/model/sql/gen"
|
|
|
|
"github.com/zeromicro/go-zero/tools/goctl/model/sql/model"
|
|
|
|
"github.com/zeromicro/go-zero/tools/goctl/model/sql/util"
|
|
|
|
file "github.com/zeromicro/go-zero/tools/goctl/util"
|
|
|
|
"github.com/zeromicro/go-zero/tools/goctl/util/console"
|
|
|
|
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// VarStringSrc describes the source file of sql.
|
|
|
|
VarStringSrc string
|
|
|
|
// VarStringDir describes the output directory of sql.
|
|
|
|
VarStringDir string
|
|
|
|
// VarBoolCache describes whether the cache is enabled.
|
|
|
|
VarBoolCache bool
|
|
|
|
// VarBoolIdea describes whether is idea or not.
|
|
|
|
VarBoolIdea bool
|
|
|
|
// VarStringURL describes the dsn of the sql.
|
|
|
|
VarStringURL string
|
|
|
|
// VarStringSliceTable describes tables.
|
|
|
|
VarStringSliceTable []string
|
|
|
|
// VarStringTable describes a table of sql.
|
|
|
|
VarStringTable string
|
|
|
|
// VarStringStyle describes the style.
|
|
|
|
VarStringStyle string
|
|
|
|
// VarStringDatabase describes the database.
|
|
|
|
VarStringDatabase string
|
|
|
|
// VarStringSchema describes the schema of postgresql.
|
|
|
|
VarStringSchema string
|
|
|
|
// VarStringHome describes the goctl home.
|
|
|
|
VarStringHome string
|
|
|
|
// VarStringRemote describes the remote git repository.
|
|
|
|
VarStringRemote string
|
|
|
|
// VarStringBranch describes the git branch of the repository.
|
|
|
|
VarStringBranch string
|
|
|
|
// VarBoolStrict describes whether the strict mode is enabled.
|
|
|
|
VarBoolStrict bool
|
|
|
|
// VarStringSliceIgnoreColumns represents the columns which are ignored.
|
|
|
|
VarStringSliceIgnoreColumns []string
|
|
|
|
)
|
|
|
|
|
|
|
|
var errNotMatched = errors.New("sql not matched")
|
|
|
|
|
|
|
|
// MysqlDDL generates model code from ddl
|
|
|
|
func MysqlDDL(_ *cobra.Command, _ []string) error {
|
|
|
|
migrationnotes.BeforeCommands(VarStringDir, VarStringStyle)
|
|
|
|
src := VarStringSrc
|
|
|
|
dir := VarStringDir
|
|
|
|
cache := VarBoolCache
|
|
|
|
idea := VarBoolIdea
|
|
|
|
style := VarStringStyle
|
|
|
|
database := VarStringDatabase
|
|
|
|
home := VarStringHome
|
|
|
|
remote := VarStringRemote
|
|
|
|
branch := VarStringBranch
|
|
|
|
if len(remote) > 0 {
|
|
|
|
repo, _ := file.CloneIntoGitHome(remote, branch)
|
|
|
|
if len(repo) > 0 {
|
|
|
|
home = repo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(home) > 0 {
|
|
|
|
pathx.RegisterGoctlHome(home)
|
|
|
|
}
|
|
|
|
cfg, err := config.NewConfig(style)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
arg := ddlArg{
|
|
|
|
src: src,
|
|
|
|
dir: dir,
|
|
|
|
cfg: cfg,
|
|
|
|
cache: cache,
|
|
|
|
idea: idea,
|
|
|
|
database: database,
|
|
|
|
strict: VarBoolStrict,
|
|
|
|
ignoreColumns: mergeColumns(VarStringSliceIgnoreColumns),
|
|
|
|
}
|
|
|
|
return fromDDL(arg)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MySqlDataSource generates model code from datasource
|
|
|
|
func MySqlDataSource(_ *cobra.Command, _ []string) error {
|
|
|
|
migrationnotes.BeforeCommands(VarStringDir, VarStringStyle)
|
|
|
|
url := strings.TrimSpace(VarStringURL)
|
|
|
|
dir := strings.TrimSpace(VarStringDir)
|
|
|
|
cache := VarBoolCache
|
|
|
|
idea := VarBoolIdea
|
|
|
|
style := VarStringStyle
|
|
|
|
home := VarStringHome
|
|
|
|
remote := VarStringRemote
|
|
|
|
branch := VarStringBranch
|
|
|
|
if len(remote) > 0 {
|
|
|
|
repo, _ := file.CloneIntoGitHome(remote, branch)
|
|
|
|
if len(repo) > 0 {
|
|
|
|
home = repo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(home) > 0 {
|
|
|
|
pathx.RegisterGoctlHome(home)
|
|
|
|
}
|
|
|
|
|
|
|
|
tableValue := VarStringSliceTable
|
|
|
|
patterns := parseTableList(tableValue)
|
|
|
|
cfg, err := config.NewConfig(style)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
arg := dataSourceArg{
|
|
|
|
url: url,
|
|
|
|
dir: dir,
|
|
|
|
tablePat: patterns,
|
|
|
|
cfg: cfg,
|
|
|
|
cache: cache,
|
|
|
|
idea: idea,
|
|
|
|
strict: VarBoolStrict,
|
|
|
|
ignoreColumns: mergeColumns(VarStringSliceIgnoreColumns),
|
|
|
|
}
|
|
|
|
return fromMysqlDataSource(arg)
|
|
|
|
}
|
|
|
|
|
|
|
|
func mergeColumns(columns []string) []string {
|
|
|
|
set := collection.NewSet()
|
|
|
|
for _, v := range columns {
|
|
|
|
fields := strings.FieldsFunc(v, func(r rune) bool {
|
|
|
|
return r == ','
|
|
|
|
})
|
|
|
|
set.AddStr(fields...)
|
|
|
|
}
|
|
|
|
return set.KeysStr()
|
|
|
|
}
|
|
|
|
|
|
|
|
type pattern map[string]struct{}
|
|
|
|
|
|
|
|
func (p pattern) Match(s string) bool {
|
|
|
|
for v := range p {
|
|
|
|
match, err := filepath.Match(v, s)
|
|
|
|
if err != nil {
|
|
|
|
console.Error("%+v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if match {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p pattern) list() []string {
|
|
|
|
var ret []string
|
|
|
|
for v := range p {
|
|
|
|
ret = append(ret, v)
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseTableList(tableValue []string) pattern {
|
|
|
|
tablePattern := make(pattern)
|
|
|
|
for _, v := range tableValue {
|
|
|
|
fields := strings.FieldsFunc(v, func(r rune) bool {
|
|
|
|
return r == ','
|
|
|
|
})
|
|
|
|
for _, f := range fields {
|
|
|
|
tablePattern[f] = struct{}{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tablePattern
|
|
|
|
}
|
|
|
|
|
|
|
|
// PostgreSqlDataSource generates model code from datasource
|
|
|
|
func PostgreSqlDataSource(_ *cobra.Command, _ []string) error {
|
|
|
|
migrationnotes.BeforeCommands(VarStringDir, VarStringStyle)
|
|
|
|
url := strings.TrimSpace(VarStringURL)
|
|
|
|
dir := strings.TrimSpace(VarStringDir)
|
|
|
|
cache := VarBoolCache
|
|
|
|
idea := VarBoolIdea
|
|
|
|
style := VarStringStyle
|
|
|
|
schema := VarStringSchema
|
|
|
|
home := VarStringHome
|
|
|
|
remote := VarStringRemote
|
|
|
|
branch := VarStringBranch
|
|
|
|
if len(remote) > 0 {
|
|
|
|
repo, _ := file.CloneIntoGitHome(remote, branch)
|
|
|
|
if len(repo) > 0 {
|
|
|
|
home = repo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(home) > 0 {
|
|
|
|
pathx.RegisterGoctlHome(home)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(schema) == 0 {
|
|
|
|
schema = "public"
|
|
|
|
}
|
|
|
|
|
|
|
|
pattern := strings.TrimSpace(VarStringTable)
|
|
|
|
cfg, err := config.NewConfig(style)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ignoreColumns := mergeColumns(VarStringSliceIgnoreColumns)
|
|
|
|
|
|
|
|
return fromPostgreSqlDataSource(url, pattern, dir, schema, cfg, cache, idea, VarBoolStrict, ignoreColumns)
|
|
|
|
}
|
|
|
|
|
|
|
|
type ddlArg struct {
|
|
|
|
src, dir string
|
|
|
|
cfg *config.Config
|
|
|
|
cache, idea bool
|
|
|
|
database string
|
|
|
|
strict bool
|
|
|
|
ignoreColumns []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func fromDDL(arg ddlArg) error {
|
|
|
|
log := console.NewConsole(arg.idea)
|
|
|
|
src := strings.TrimSpace(arg.src)
|
|
|
|
if len(src) == 0 {
|
|
|
|
return errors.New("expected path or path globbing patterns, but nothing found")
|
|
|
|
}
|
|
|
|
|
|
|
|
files, err := util.MatchFiles(src)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(files) == 0 {
|
|
|
|
return errNotMatched
|
|
|
|
}
|
|
|
|
|
|
|
|
generator, err := gen.NewDefaultGenerator(arg.dir, arg.cfg,
|
|
|
|
gen.WithConsoleOption(log), gen.WithIgnoreColumns(arg.ignoreColumns))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, file := range files {
|
|
|
|
err = generator.StartFromDDL(file, arg.cache, arg.strict, arg.database)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type dataSourceArg struct {
|
|
|
|
url, dir string
|
|
|
|
tablePat pattern
|
|
|
|
cfg *config.Config
|
|
|
|
cache, idea bool
|
|
|
|
strict bool
|
|
|
|
ignoreColumns []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func fromMysqlDataSource(arg dataSourceArg) error {
|
|
|
|
log := console.NewConsole(arg.idea)
|
|
|
|
if len(arg.url) == 0 {
|
|
|
|
log.Error("%v", "expected data source of mysql, but nothing found")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(arg.tablePat) == 0 {
|
|
|
|
log.Error("%v", "expected table or table globbing patterns, but nothing found")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
dsn, err := mysql.ParseDSN(arg.url)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
logx.Disable()
|
|
|
|
databaseSource := strings.TrimSuffix(arg.url, "/"+dsn.DBName) + "/information_schema"
|
|
|
|
db := sqlx.NewMysql(databaseSource)
|
|
|
|
im := model.NewInformationSchemaModel(db)
|
|
|
|
|
|
|
|
tables, err := im.GetAllTables(dsn.DBName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
matchTables := make(map[string]*model.Table)
|
|
|
|
for _, item := range tables {
|
|
|
|
if !arg.tablePat.Match(item) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
columnData, err := im.FindColumns(dsn.DBName, item)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
table, err := columnData.Convert()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
matchTables[item] = table
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(matchTables) == 0 {
|
|
|
|
return errors.New("no tables matched")
|
|
|
|
}
|
|
|
|
|
|
|
|
generator, err := gen.NewDefaultGenerator(arg.dir, arg.cfg,
|
|
|
|
gen.WithConsoleOption(log), gen.WithIgnoreColumns(arg.ignoreColumns))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return generator.StartFromInformationSchema(matchTables, arg.cache, arg.strict)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fromPostgreSqlDataSource(url, pattern, dir, schema string, cfg *config.Config, cache, idea, strict bool, ignoreColumns []string) error {
|
|
|
|
log := console.NewConsole(idea)
|
|
|
|
if len(url) == 0 {
|
|
|
|
log.Error("%v", "expected data source of postgresql, but nothing found")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(pattern) == 0 {
|
|
|
|
log.Error("%v", "expected table or table globbing patterns, but nothing found")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
db := postgres.New(url)
|
|
|
|
im := model.NewPostgreSqlModel(db)
|
|
|
|
|
|
|
|
tables, err := im.GetAllTables(schema)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
matchTables := make(map[string]*model.Table)
|
|
|
|
for _, item := range tables {
|
|
|
|
match, err := filepath.Match(pattern, item)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !match {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
columnData, err := im.FindColumns(schema, item)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
table, err := columnData.Convert()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
matchTables[item] = table
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(matchTables) == 0 {
|
|
|
|
return errors.New("no tables matched")
|
|
|
|
}
|
|
|
|
|
|
|
|
generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log), gen.WithPostgreSql(), gen.WithIgnoreColumns(ignoreColumns))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return generator.StartFromInformationSchema(matchTables, cache, strict)
|
|
|
|
}
|