command.go 8.2 KB

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