parser.go 9.1 KB

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