parser.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. package parser
  2. import (
  3. "fmt"
  4. "path/filepath"
  5. "sort"
  6. "strings"
  7. "github.com/tal-tech/go-zero/core/collection"
  8. "github.com/tal-tech/go-zero/tools/goctl/model/sql/converter"
  9. "github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
  10. "github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
  11. su "github.com/tal-tech/go-zero/tools/goctl/util"
  12. "github.com/tal-tech/go-zero/tools/goctl/util/console"
  13. "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
  14. "github.com/zeromicro/ddl-parser/parser"
  15. )
  16. const timeImport = "time.Time"
  17. type (
  18. // Table describes a mysql table
  19. Table struct {
  20. Name stringx.String
  21. Db stringx.String
  22. PrimaryKey Primary
  23. UniqueIndex map[string][]*Field
  24. Fields []*Field
  25. }
  26. // Primary describes a primary key
  27. Primary struct {
  28. Field
  29. AutoIncrement bool
  30. }
  31. // Field describes a table field
  32. Field struct {
  33. NameOriginal string
  34. Name stringx.String
  35. DataType string
  36. Comment string
  37. SeqInIndex int
  38. OrdinalPosition int
  39. }
  40. // KeyType types alias of int
  41. KeyType int
  42. )
  43. func parseNameOriginal(ts []*parser.Table) (nameOriginals [][]string) {
  44. var columns []string
  45. for _, t := range ts {
  46. columns = []string{}
  47. for _, c := range t.Columns {
  48. columns = append(columns, c.Name)
  49. }
  50. nameOriginals = append(nameOriginals, columns)
  51. }
  52. return
  53. }
  54. // Parse parses ddl into golang structure
  55. func Parse(filename, database string) ([]*Table, error) {
  56. p := parser.NewParser()
  57. ts, err := p.From(filename)
  58. if err != nil {
  59. return nil, err
  60. }
  61. nameOriginals := parseNameOriginal(ts)
  62. tables := GetSafeTables(ts)
  63. indexNameGen := func(column ...string) string {
  64. return strings.Join(column, "_")
  65. }
  66. prefix := filepath.Base(filename)
  67. var list []*Table
  68. for indexTable, e := range tables {
  69. columns := e.Columns
  70. var (
  71. primaryColumnSet = collection.NewSet()
  72. primaryColumn string
  73. uniqueKeyMap = make(map[string][]string)
  74. normalKeyMap = make(map[string][]string)
  75. )
  76. for _, column := range columns {
  77. if column.Constraint != nil {
  78. if column.Constraint.Primary {
  79. primaryColumnSet.AddStr(column.Name)
  80. }
  81. if column.Constraint.Unique {
  82. indexName := indexNameGen(column.Name, "unique")
  83. uniqueKeyMap[indexName] = []string{column.Name}
  84. }
  85. if column.Constraint.Key {
  86. indexName := indexNameGen(column.Name, "idx")
  87. uniqueKeyMap[indexName] = []string{column.Name}
  88. }
  89. }
  90. }
  91. for _, e := range e.Constraints {
  92. if len(e.ColumnPrimaryKey) > 1 {
  93. return nil, fmt.Errorf("%s: unexpected join primary key", prefix)
  94. }
  95. if len(e.ColumnPrimaryKey) == 1 {
  96. primaryColumn = e.ColumnPrimaryKey[0]
  97. primaryColumnSet.AddStr(e.ColumnPrimaryKey[0])
  98. }
  99. if len(e.ColumnUniqueKey) > 0 {
  100. list := append([]string(nil), e.ColumnUniqueKey...)
  101. list = append(list, "unique")
  102. indexName := indexNameGen(list...)
  103. uniqueKeyMap[indexName] = e.ColumnUniqueKey
  104. }
  105. }
  106. if primaryColumnSet.Count() > 1 {
  107. return nil, fmt.Errorf("%s: unexpected join primary key", prefix)
  108. }
  109. primaryKey, fieldM, err := convertColumns(columns, primaryColumn)
  110. if err != nil {
  111. return nil, err
  112. }
  113. var fields []*Field
  114. // sort
  115. for indexColumn, c := range columns {
  116. field, ok := fieldM[c.Name]
  117. if ok {
  118. field.NameOriginal = nameOriginals[indexTable][indexColumn]
  119. fields = append(fields, field)
  120. }
  121. }
  122. var (
  123. uniqueIndex = make(map[string][]*Field)
  124. normalIndex = make(map[string][]*Field)
  125. )
  126. for indexName, each := range uniqueKeyMap {
  127. for _, columnName := range each {
  128. uniqueIndex[indexName] = append(uniqueIndex[indexName], fieldM[columnName])
  129. }
  130. }
  131. for indexName, each := range normalKeyMap {
  132. for _, columnName := range each {
  133. normalIndex[indexName] = append(normalIndex[indexName], fieldM[columnName])
  134. }
  135. }
  136. checkDuplicateUniqueIndex(uniqueIndex, e.Name)
  137. list = append(list, &Table{
  138. Name: stringx.From(e.Name),
  139. Db: stringx.From(database),
  140. PrimaryKey: primaryKey,
  141. UniqueIndex: uniqueIndex,
  142. Fields: fields,
  143. })
  144. }
  145. return list, nil
  146. }
  147. func checkDuplicateUniqueIndex(uniqueIndex map[string][]*Field, tableName string) {
  148. log := console.NewColorConsole()
  149. uniqueSet := collection.NewSet()
  150. for k, i := range uniqueIndex {
  151. var list []string
  152. for _, e := range i {
  153. list = append(list, e.Name.Source())
  154. }
  155. joinRet := strings.Join(list, ",")
  156. if uniqueSet.Contains(joinRet) {
  157. log.Warning("[checkDuplicateUniqueIndex]: table %s: duplicate unique index %s", tableName, joinRet)
  158. delete(uniqueIndex, k)
  159. continue
  160. }
  161. uniqueSet.AddStr(joinRet)
  162. }
  163. }
  164. func convertColumns(columns []*parser.Column, primaryColumn string) (Primary, map[string]*Field, error) {
  165. var (
  166. primaryKey Primary
  167. fieldM = make(map[string]*Field)
  168. log = console.NewColorConsole()
  169. )
  170. for _, column := range columns {
  171. if column == nil {
  172. continue
  173. }
  174. var (
  175. comment string
  176. isDefaultNull bool
  177. )
  178. if column.Constraint != nil {
  179. comment = column.Constraint.Comment
  180. isDefaultNull = !column.Constraint.NotNull
  181. if !column.Constraint.NotNull && column.Constraint.HasDefaultValue {
  182. isDefaultNull = false
  183. }
  184. if column.Name == primaryColumn {
  185. isDefaultNull = false
  186. }
  187. }
  188. dataType, err := converter.ConvertDataType(column.DataType.Type(), isDefaultNull)
  189. if err != nil {
  190. return Primary{}, nil, err
  191. }
  192. if column.Constraint != nil {
  193. if column.Name == primaryColumn {
  194. if !column.Constraint.AutoIncrement && dataType == "int64" {
  195. log.Warning("[convertColumns]: The primary key %q is recommended to add constraint `AUTO_INCREMENT`", column.Name)
  196. }
  197. } else if column.Constraint.NotNull && !column.Constraint.HasDefaultValue {
  198. log.Warning("[convertColumns]: The column %q is recommended to add constraint `DEFAULT`", column.Name)
  199. }
  200. }
  201. var field Field
  202. field.Name = stringx.From(column.Name)
  203. field.DataType = dataType
  204. field.Comment = util.TrimNewLine(comment)
  205. if field.Name.Source() == primaryColumn {
  206. primaryKey = Primary{
  207. Field: field,
  208. }
  209. if column.Constraint != nil {
  210. primaryKey.AutoIncrement = column.Constraint.AutoIncrement
  211. }
  212. }
  213. fieldM[field.Name.Source()] = &field
  214. }
  215. return primaryKey, fieldM, nil
  216. }
  217. // ContainsTime returns true if contains golang type time.Time
  218. func (t *Table) ContainsTime() bool {
  219. for _, item := range t.Fields {
  220. if item.DataType == timeImport {
  221. return true
  222. }
  223. }
  224. return false
  225. }
  226. // ConvertDataType converts mysql data type into golang data type
  227. func ConvertDataType(table *model.Table) (*Table, error) {
  228. isPrimaryDefaultNull := table.PrimaryKey.ColumnDefault == nil && table.PrimaryKey.IsNullAble == "YES"
  229. primaryDataType, err := converter.ConvertStringDataType(table.PrimaryKey.DataType, isPrimaryDefaultNull)
  230. if err != nil {
  231. return nil, err
  232. }
  233. var reply Table
  234. reply.UniqueIndex = map[string][]*Field{}
  235. reply.Name = stringx.From(table.Table)
  236. reply.Db = stringx.From(table.Db)
  237. seqInIndex := 0
  238. if table.PrimaryKey.Index != nil {
  239. seqInIndex = table.PrimaryKey.Index.SeqInIndex
  240. }
  241. reply.PrimaryKey = Primary{
  242. Field: Field{
  243. Name: stringx.From(table.PrimaryKey.Name),
  244. DataType: primaryDataType,
  245. Comment: table.PrimaryKey.Comment,
  246. SeqInIndex: seqInIndex,
  247. OrdinalPosition: table.PrimaryKey.OrdinalPosition,
  248. },
  249. AutoIncrement: strings.Contains(table.PrimaryKey.Extra, "auto_increment"),
  250. }
  251. fieldM, err := getTableFields(table)
  252. if err != nil {
  253. return nil, err
  254. }
  255. for _, each := range fieldM {
  256. reply.Fields = append(reply.Fields, each)
  257. }
  258. sort.Slice(reply.Fields, func(i, j int) bool {
  259. return reply.Fields[i].OrdinalPosition < reply.Fields[j].OrdinalPosition
  260. })
  261. uniqueIndexSet := collection.NewSet()
  262. log := console.NewColorConsole()
  263. for indexName, each := range table.UniqueIndex {
  264. sort.Slice(each, func(i, j int) bool {
  265. if each[i].Index != nil {
  266. return each[i].Index.SeqInIndex < each[j].Index.SeqInIndex
  267. }
  268. return false
  269. })
  270. if len(each) == 1 {
  271. one := each[0]
  272. if one.Name == table.PrimaryKey.Name {
  273. log.Warning("[ConvertDataType]: table q%, duplicate unique index with primary key: %q", table.Table, one.Name)
  274. continue
  275. }
  276. }
  277. var list []*Field
  278. var uniqueJoin []string
  279. for _, c := range each {
  280. list = append(list, fieldM[c.Name])
  281. uniqueJoin = append(uniqueJoin, c.Name)
  282. }
  283. uniqueKey := strings.Join(uniqueJoin, ",")
  284. if uniqueIndexSet.Contains(uniqueKey) {
  285. log.Warning("[ConvertDataType]: table %q, duplicate unique index %q", table.Table, uniqueKey)
  286. continue
  287. }
  288. uniqueIndexSet.AddStr(uniqueKey)
  289. reply.UniqueIndex[indexName] = list
  290. }
  291. return &reply, nil
  292. }
  293. func getTableFields(table *model.Table) (map[string]*Field, error) {
  294. fieldM := make(map[string]*Field)
  295. for _, each := range table.Columns {
  296. isDefaultNull := each.ColumnDefault == nil && each.IsNullAble == "YES"
  297. dt, err := converter.ConvertStringDataType(each.DataType, isDefaultNull)
  298. if err != nil {
  299. return nil, err
  300. }
  301. columnSeqInIndex := 0
  302. if each.Index != nil {
  303. columnSeqInIndex = each.Index.SeqInIndex
  304. }
  305. field := &Field{
  306. NameOriginal: each.Name,
  307. Name: stringx.From(each.Name),
  308. DataType: dt,
  309. Comment: each.Comment,
  310. SeqInIndex: columnSeqInIndex,
  311. OrdinalPosition: each.OrdinalPosition,
  312. }
  313. fieldM[each.Name] = field
  314. }
  315. return fieldM, nil
  316. }
  317. // GetSafeTables escapes the golang keywords from sql tables.
  318. func GetSafeTables(tables []*parser.Table) []*parser.Table {
  319. var list []*parser.Table
  320. for _, t := range tables {
  321. table := GetSafeTable(t)
  322. list = append(list, table)
  323. }
  324. return list
  325. }
  326. // GetSafeTable escapes the golang keywords from sql table.
  327. func GetSafeTable(table *parser.Table) *parser.Table {
  328. table.Name = su.EscapeGolangKeyword(table.Name)
  329. for _, c := range table.Columns {
  330. c.Name = su.EscapeGolangKeyword(c.Name)
  331. }
  332. for _, e := range table.Constraints {
  333. var uniqueKeys, primaryKeys []string
  334. for _, u := range e.ColumnUniqueKey {
  335. uniqueKeys = append(uniqueKeys, su.EscapeGolangKeyword(u))
  336. }
  337. for _, p := range e.ColumnPrimaryKey {
  338. primaryKeys = append(primaryKeys, su.EscapeGolangKeyword(p))
  339. }
  340. e.ColumnUniqueKey = uniqueKeys
  341. e.ColumnPrimaryKey = primaryKeys
  342. }
  343. return table
  344. }