parser.go 9.9 KB

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