parser.go 10 KB

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