command.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. package command
  2. import (
  3. "errors"
  4. "path/filepath"
  5. "strings"
  6. "github.com/go-sql-driver/mysql"
  7. "github.com/spf13/cobra"
  8. "github.com/wuntsong-org/go-zero-plus/core/collection"
  9. "github.com/wuntsong-org/go-zero-plus/core/logx"
  10. "github.com/wuntsong-org/go-zero-plus/core/stores/postgres"
  11. "github.com/wuntsong-org/go-zero-plus/core/stores/sqlx"
  12. "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/config"
  13. "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/model/sql/command/migrationnotes"
  14. "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/model/sql/gen"
  15. "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/model/sql/model"
  16. "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/model/sql/util"
  17. file "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/util"
  18. "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/util/console"
  19. "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/util/pathx"
  20. )
  21. var (
  22. // VarStringSrc describes the source file of sql.
  23. VarStringSrc string
  24. // VarStringDir describes the output directory of sql.
  25. VarStringDir string
  26. // VarBoolCache describes whether the cache is enabled.
  27. VarBoolCache bool
  28. // VarBoolIdea describes whether is idea or not.
  29. VarBoolIdea bool
  30. // VarStringURL describes the dsn of the sql.
  31. VarStringURL string
  32. // VarStringSliceTable describes tables.
  33. VarStringSliceTable []string
  34. // VarStringStyle describes the style.
  35. VarStringStyle string
  36. // VarStringDatabase describes the database.
  37. VarStringDatabase string
  38. // VarStringSchema describes the schema of postgresql.
  39. VarStringSchema string
  40. // VarStringHome describes the goctl home.
  41. VarStringHome string
  42. // VarStringRemote describes the remote git repository.
  43. VarStringRemote string
  44. // VarStringBranch describes the git branch of the repository.
  45. VarStringBranch string
  46. // VarBoolStrict describes whether the strict mode is enabled.
  47. VarBoolStrict bool
  48. // VarStringSliceIgnoreColumns represents the columns which are ignored.
  49. VarStringSliceIgnoreColumns []string
  50. )
  51. var errNotMatched = errors.New("sql not matched")
  52. // MysqlDDL generates model code from ddl
  53. func MysqlDDL(_ *cobra.Command, _ []string) error {
  54. migrationnotes.BeforeCommands(VarStringDir, VarStringStyle)
  55. src := VarStringSrc
  56. dir := VarStringDir
  57. cache := VarBoolCache
  58. idea := VarBoolIdea
  59. style := VarStringStyle
  60. database := VarStringDatabase
  61. home := VarStringHome
  62. remote := VarStringRemote
  63. branch := VarStringBranch
  64. if len(remote) > 0 {
  65. repo, _ := file.CloneIntoGitHome(remote, branch)
  66. if len(repo) > 0 {
  67. home = repo
  68. }
  69. }
  70. if len(home) > 0 {
  71. pathx.RegisterGoctlHome(home)
  72. }
  73. cfg, err := config.NewConfig(style)
  74. if err != nil {
  75. return err
  76. }
  77. arg := ddlArg{
  78. src: src,
  79. dir: dir,
  80. cfg: cfg,
  81. cache: cache,
  82. idea: idea,
  83. database: database,
  84. strict: VarBoolStrict,
  85. ignoreColumns: mergeColumns(VarStringSliceIgnoreColumns),
  86. }
  87. return fromDDL(arg)
  88. }
  89. // MySqlDataSource generates model code from datasource
  90. func MySqlDataSource(_ *cobra.Command, _ []string) error {
  91. migrationnotes.BeforeCommands(VarStringDir, VarStringStyle)
  92. url := strings.TrimSpace(VarStringURL)
  93. dir := strings.TrimSpace(VarStringDir)
  94. cache := VarBoolCache
  95. idea := VarBoolIdea
  96. style := VarStringStyle
  97. home := VarStringHome
  98. remote := VarStringRemote
  99. branch := VarStringBranch
  100. if len(remote) > 0 {
  101. repo, _ := file.CloneIntoGitHome(remote, branch)
  102. if len(repo) > 0 {
  103. home = repo
  104. }
  105. }
  106. if len(home) > 0 {
  107. pathx.RegisterGoctlHome(home)
  108. }
  109. tableValue := VarStringSliceTable
  110. patterns := parseTableList(tableValue)
  111. cfg, err := config.NewConfig(style)
  112. if err != nil {
  113. return err
  114. }
  115. arg := dataSourceArg{
  116. url: url,
  117. dir: dir,
  118. tablePat: patterns,
  119. cfg: cfg,
  120. cache: cache,
  121. idea: idea,
  122. strict: VarBoolStrict,
  123. ignoreColumns: mergeColumns(VarStringSliceIgnoreColumns),
  124. }
  125. return fromMysqlDataSource(arg)
  126. }
  127. func mergeColumns(columns []string) []string {
  128. set := collection.NewSet()
  129. for _, v := range columns {
  130. fields := strings.FieldsFunc(v, func(r rune) bool {
  131. return r == ','
  132. })
  133. set.AddStr(fields...)
  134. }
  135. return set.KeysStr()
  136. }
  137. type pattern map[string]struct{}
  138. func (p pattern) Match(s string) bool {
  139. for v := range p {
  140. match, err := filepath.Match(v, s)
  141. if err != nil {
  142. console.Error("%+v", err)
  143. continue
  144. }
  145. if match {
  146. return true
  147. }
  148. }
  149. return false
  150. }
  151. func (p pattern) list() []string {
  152. var ret []string
  153. for v := range p {
  154. ret = append(ret, v)
  155. }
  156. return ret
  157. }
  158. func parseTableList(tableValue []string) pattern {
  159. tablePattern := make(pattern)
  160. for _, v := range tableValue {
  161. fields := strings.FieldsFunc(v, func(r rune) bool {
  162. return r == ','
  163. })
  164. for _, f := range fields {
  165. tablePattern[f] = struct{}{}
  166. }
  167. }
  168. return tablePattern
  169. }
  170. // PostgreSqlDataSource generates model code from datasource
  171. func PostgreSqlDataSource(_ *cobra.Command, _ []string) error {
  172. migrationnotes.BeforeCommands(VarStringDir, VarStringStyle)
  173. url := strings.TrimSpace(VarStringURL)
  174. dir := strings.TrimSpace(VarStringDir)
  175. cache := VarBoolCache
  176. idea := VarBoolIdea
  177. style := VarStringStyle
  178. schema := VarStringSchema
  179. home := VarStringHome
  180. remote := VarStringRemote
  181. branch := VarStringBranch
  182. if len(remote) > 0 {
  183. repo, _ := file.CloneIntoGitHome(remote, branch)
  184. if len(repo) > 0 {
  185. home = repo
  186. }
  187. }
  188. if len(home) > 0 {
  189. pathx.RegisterGoctlHome(home)
  190. }
  191. if len(schema) == 0 {
  192. schema = "public"
  193. }
  194. patterns := parseTableList(VarStringSliceTable)
  195. cfg, err := config.NewConfig(style)
  196. if err != nil {
  197. return err
  198. }
  199. ignoreColumns := mergeColumns(VarStringSliceIgnoreColumns)
  200. return fromPostgreSqlDataSource(url, patterns, dir, schema, cfg, cache, idea, VarBoolStrict, ignoreColumns)
  201. }
  202. type ddlArg struct {
  203. src, dir string
  204. cfg *config.Config
  205. cache, idea bool
  206. database string
  207. strict bool
  208. ignoreColumns []string
  209. }
  210. func fromDDL(arg ddlArg) error {
  211. log := console.NewConsole(arg.idea)
  212. src := strings.TrimSpace(arg.src)
  213. if len(src) == 0 {
  214. return errors.New("expected path or path globbing patterns, but nothing found")
  215. }
  216. files, err := util.MatchFiles(src)
  217. if err != nil {
  218. return err
  219. }
  220. if len(files) == 0 {
  221. return errNotMatched
  222. }
  223. generator, err := gen.NewDefaultGenerator(arg.dir, arg.cfg,
  224. gen.WithConsoleOption(log), gen.WithIgnoreColumns(arg.ignoreColumns))
  225. if err != nil {
  226. return err
  227. }
  228. for _, file := range files {
  229. err = generator.StartFromDDL(file, arg.cache, arg.strict, arg.database)
  230. if err != nil {
  231. return err
  232. }
  233. }
  234. return nil
  235. }
  236. type dataSourceArg struct {
  237. url, dir string
  238. tablePat pattern
  239. cfg *config.Config
  240. cache, idea bool
  241. strict bool
  242. ignoreColumns []string
  243. }
  244. func fromMysqlDataSource(arg dataSourceArg) error {
  245. log := console.NewConsole(arg.idea)
  246. if len(arg.url) == 0 {
  247. log.Error("%v", "expected data source of mysql, but nothing found")
  248. return nil
  249. }
  250. if len(arg.tablePat) == 0 {
  251. log.Error("%v", "expected table or table globbing patterns, but nothing found")
  252. return nil
  253. }
  254. dsn, err := mysql.ParseDSN(arg.url)
  255. if err != nil {
  256. return err
  257. }
  258. logx.Disable()
  259. databaseSource := strings.TrimSuffix(arg.url, "/"+dsn.DBName) + "/information_schema"
  260. db := sqlx.NewMysql(databaseSource)
  261. im := model.NewInformationSchemaModel(db)
  262. tables, err := im.GetAllTables(dsn.DBName)
  263. if err != nil {
  264. return err
  265. }
  266. matchTables := make(map[string]*model.Table)
  267. for _, item := range tables {
  268. if !arg.tablePat.Match(item) {
  269. continue
  270. }
  271. columnData, err := im.FindColumns(dsn.DBName, item)
  272. if err != nil {
  273. return err
  274. }
  275. table, err := columnData.Convert()
  276. if err != nil {
  277. return err
  278. }
  279. matchTables[item] = table
  280. }
  281. if len(matchTables) == 0 {
  282. return errors.New("no tables matched")
  283. }
  284. generator, err := gen.NewDefaultGenerator(arg.dir, arg.cfg,
  285. gen.WithConsoleOption(log), gen.WithIgnoreColumns(arg.ignoreColumns))
  286. if err != nil {
  287. return err
  288. }
  289. return generator.StartFromInformationSchema(matchTables, arg.cache, arg.strict)
  290. }
  291. func fromPostgreSqlDataSource(url string, pattern pattern, dir, schema string, cfg *config.Config, cache, idea, strict bool, ignoreColumns []string) error {
  292. log := console.NewConsole(idea)
  293. if len(url) == 0 {
  294. log.Error("%v", "expected data source of postgresql, but nothing found")
  295. return nil
  296. }
  297. if len(pattern) == 0 {
  298. log.Error("%v", "expected table or table globbing patterns, but nothing found")
  299. return nil
  300. }
  301. db := postgres.New(url)
  302. im := model.NewPostgreSqlModel(db)
  303. tables, err := im.GetAllTables(schema)
  304. if err != nil {
  305. return err
  306. }
  307. matchTables := make(map[string]*model.Table)
  308. for _, item := range tables {
  309. if !pattern.Match(item) {
  310. continue
  311. }
  312. columnData, err := im.FindColumns(schema, item)
  313. if err != nil {
  314. return err
  315. }
  316. table, err := columnData.Convert()
  317. if err != nil {
  318. return err
  319. }
  320. matchTables[item] = table
  321. }
  322. if len(matchTables) == 0 {
  323. return errors.New("no tables matched")
  324. }
  325. generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log), gen.WithPostgreSql(), gen.WithIgnoreColumns(ignoreColumns))
  326. if err != nil {
  327. return err
  328. }
  329. return generator.StartFromInformationSchema(matchTables, cache, strict)
  330. }