parser.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. package parser
  2. import (
  3. "fmt"
  4. "sort"
  5. "strings"
  6. "git.i2edu.net/i2/go-zero/core/collection"
  7. "git.i2edu.net/i2/go-zero/tools/goctl/model/sql/converter"
  8. "git.i2edu.net/i2/go-zero/tools/goctl/model/sql/model"
  9. "git.i2edu.net/i2/go-zero/tools/goctl/model/sql/util"
  10. "git.i2edu.net/i2/go-zero/tools/goctl/util/console"
  11. "git.i2edu.net/i2/go-zero/tools/goctl/util/stringx"
  12. "github.com/xwb1989/sqlparser"
  13. )
  14. const timeImport = "time.Time"
  15. type (
  16. // Table describes a mysql table
  17. Table struct {
  18. Name stringx.String
  19. PrimaryKey Primary
  20. UniqueIndex map[string][]*Field
  21. NormalIndex map[string][]*Field
  22. Fields []*Field
  23. }
  24. // Primary describes a primary key
  25. Primary struct {
  26. Field
  27. AutoIncrement bool
  28. }
  29. // Field describes a table field
  30. Field struct {
  31. Name stringx.String
  32. DataBaseType string
  33. DataType string
  34. Comment string
  35. SeqInIndex int
  36. OrdinalPosition int
  37. }
  38. // KeyType types alias of int
  39. KeyType int
  40. )
  41. // Parse parses ddl into golang structure
  42. func Parse(ddl string) (*Table, error) {
  43. stmt, err := sqlparser.ParseStrictDDL(ddl)
  44. if err != nil {
  45. return nil, err
  46. }
  47. ddlStmt, ok := stmt.(*sqlparser.DDL)
  48. if !ok {
  49. return nil, errUnsupportDDL
  50. }
  51. action := ddlStmt.Action
  52. if action != sqlparser.CreateStr {
  53. return nil, fmt.Errorf("expected [CREATE] action,but found: %s", action)
  54. }
  55. tableName := ddlStmt.NewName.Name.String()
  56. tableSpec := ddlStmt.TableSpec
  57. if tableSpec == nil {
  58. return nil, errTableBodyNotFound
  59. }
  60. columns := tableSpec.Columns
  61. indexes := tableSpec.Indexes
  62. primaryColumn, uniqueKeyMap, normalKeyMap, err := convertIndexes(indexes)
  63. if err != nil {
  64. return nil, err
  65. }
  66. primaryKey, fieldM, err := convertColumns(columns, primaryColumn)
  67. if err != nil {
  68. return nil, err
  69. }
  70. var fields []*Field
  71. for _, e := range fieldM {
  72. fields = append(fields, e)
  73. }
  74. var (
  75. uniqueIndex = make(map[string][]*Field)
  76. normalIndex = make(map[string][]*Field)
  77. )
  78. for indexName, each := range uniqueKeyMap {
  79. for _, columnName := range each {
  80. uniqueIndex[indexName] = append(uniqueIndex[indexName], fieldM[columnName])
  81. }
  82. }
  83. for indexName, each := range normalKeyMap {
  84. for _, columnName := range each {
  85. normalIndex[indexName] = append(normalIndex[indexName], fieldM[columnName])
  86. }
  87. }
  88. checkDuplicateUniqueIndex(uniqueIndex, tableName, normalIndex)
  89. return &Table{
  90. Name: stringx.From(tableName),
  91. PrimaryKey: primaryKey,
  92. UniqueIndex: uniqueIndex,
  93. NormalIndex: normalIndex,
  94. Fields: fields,
  95. }, nil
  96. }
  97. func checkDuplicateUniqueIndex(uniqueIndex map[string][]*Field, tableName string, normalIndex map[string][]*Field) {
  98. log := console.NewColorConsole()
  99. uniqueSet := collection.NewSet()
  100. for k, i := range uniqueIndex {
  101. var list []string
  102. for _, e := range i {
  103. list = append(list, e.Name.Source())
  104. }
  105. joinRet := strings.Join(list, ",")
  106. if uniqueSet.Contains(joinRet) {
  107. log.Warning("table %s: duplicate unique index %s", tableName, joinRet)
  108. delete(uniqueIndex, k)
  109. continue
  110. }
  111. uniqueSet.AddStr(joinRet)
  112. }
  113. normalIndexSet := collection.NewSet()
  114. for k, i := range normalIndex {
  115. var list []string
  116. for _, e := range i {
  117. list = append(list, e.Name.Source())
  118. }
  119. joinRet := strings.Join(list, ",")
  120. if normalIndexSet.Contains(joinRet) {
  121. log.Warning("table %s: duplicate index %s", tableName, joinRet)
  122. delete(normalIndex, k)
  123. continue
  124. }
  125. normalIndexSet.Add(joinRet)
  126. }
  127. }
  128. func convertColumns(columns []*sqlparser.ColumnDefinition, primaryColumn string) (Primary, map[string]*Field, error) {
  129. var (
  130. primaryKey Primary
  131. fieldM = make(map[string]*Field)
  132. )
  133. for _, column := range columns {
  134. if column == nil {
  135. continue
  136. }
  137. var comment string
  138. if column.Type.Comment != nil {
  139. comment = string(column.Type.Comment.Val)
  140. }
  141. isDefaultNull := true
  142. if column.Type.NotNull {
  143. isDefaultNull = false
  144. } else {
  145. if column.Type.Default == nil {
  146. isDefaultNull = false
  147. } else if string(column.Type.Default.Val) != "null" {
  148. isDefaultNull = false
  149. }
  150. }
  151. dataType, err := converter.ConvertDataType(column.Type.Type, isDefaultNull)
  152. if err != nil {
  153. return Primary{}, nil, err
  154. }
  155. var field Field
  156. field.Name = stringx.From(column.Name.String())
  157. field.DataBaseType = column.Type.Type
  158. field.DataType = dataType
  159. field.Comment = util.TrimNewLine(comment)
  160. if field.Name.Source() == primaryColumn {
  161. primaryKey = Primary{
  162. Field: field,
  163. AutoIncrement: bool(column.Type.Autoincrement),
  164. }
  165. }
  166. fieldM[field.Name.Source()] = &field
  167. }
  168. return primaryKey, fieldM, nil
  169. }
  170. func convertIndexes(indexes []*sqlparser.IndexDefinition) (string, map[string][]string, map[string][]string, error) {
  171. var primaryColumn string
  172. uniqueKeyMap := make(map[string][]string)
  173. normalKeyMap := make(map[string][]string)
  174. isCreateTimeOrUpdateTime := func(name string) bool {
  175. camelColumnName := stringx.From(name).ToCamel()
  176. // by default, createTime|updateTime findOne is not used.
  177. return camelColumnName == "CreateTime" || camelColumnName == "UpdateTime"
  178. }
  179. for _, index := range indexes {
  180. info := index.Info
  181. if info == nil {
  182. continue
  183. }
  184. indexName := index.Info.Name.String()
  185. if info.Primary {
  186. if len(index.Columns) > 1 {
  187. return "", nil, nil, errPrimaryKey
  188. }
  189. columnName := index.Columns[0].Column.String()
  190. if isCreateTimeOrUpdateTime(columnName) {
  191. continue
  192. }
  193. primaryColumn = columnName
  194. continue
  195. } else if info.Unique {
  196. for _, each := range index.Columns {
  197. columnName := each.Column.String()
  198. if isCreateTimeOrUpdateTime(columnName) {
  199. break
  200. }
  201. uniqueKeyMap[indexName] = append(uniqueKeyMap[indexName], columnName)
  202. }
  203. } else if info.Spatial {
  204. // do nothing
  205. } else {
  206. for _, each := range index.Columns {
  207. columnName := each.Column.String()
  208. if isCreateTimeOrUpdateTime(columnName) {
  209. break
  210. }
  211. normalKeyMap[indexName] = append(normalKeyMap[indexName], each.Column.String())
  212. }
  213. }
  214. }
  215. return primaryColumn, uniqueKeyMap, normalKeyMap, 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.ConvertDataType(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.NormalIndex = map[string][]*Field{}
  236. reply.Name = stringx.From(table.Table)
  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. DataBaseType: table.PrimaryKey.DataType,
  245. DataType: primaryDataType,
  246. Comment: table.PrimaryKey.Comment,
  247. SeqInIndex: seqInIndex,
  248. OrdinalPosition: table.PrimaryKey.OrdinalPosition,
  249. },
  250. AutoIncrement: strings.Contains(table.PrimaryKey.Extra, "auto_increment"),
  251. }
  252. fieldM, err := getTableFields(table)
  253. if err != nil {
  254. return nil, err
  255. }
  256. for _, each := range fieldM {
  257. reply.Fields = append(reply.Fields, each)
  258. }
  259. sort.Slice(reply.Fields, func(i, j int) bool {
  260. return reply.Fields[i].OrdinalPosition < reply.Fields[j].OrdinalPosition
  261. })
  262. uniqueIndexSet := collection.NewSet()
  263. log := console.NewColorConsole()
  264. for indexName, each := range table.UniqueIndex {
  265. sort.Slice(each, func(i, j int) bool {
  266. if each[i].Index != nil {
  267. return each[i].Index.SeqInIndex < each[j].Index.SeqInIndex
  268. }
  269. return false
  270. })
  271. if len(each) == 1 {
  272. one := each[0]
  273. if one.Name == table.PrimaryKey.Name {
  274. log.Warning("table %s: duplicate unique index with primary key, %s", table.Table, one.Name)
  275. continue
  276. }
  277. }
  278. var list []*Field
  279. var uniqueJoin []string
  280. for _, c := range each {
  281. list = append(list, fieldM[c.Name])
  282. uniqueJoin = append(uniqueJoin, c.Name)
  283. }
  284. uniqueKey := strings.Join(uniqueJoin, ",")
  285. if uniqueIndexSet.Contains(uniqueKey) {
  286. log.Warning("table %s: duplicate unique index, %s", table.Table, uniqueKey)
  287. continue
  288. }
  289. uniqueIndexSet.AddStr(uniqueKey)
  290. reply.UniqueIndex[indexName] = list
  291. }
  292. normalIndexSet := collection.NewSet()
  293. for indexName, each := range table.NormalIndex {
  294. var list []*Field
  295. var normalJoin []string
  296. for _, c := range each {
  297. list = append(list, fieldM[c.Name])
  298. normalJoin = append(normalJoin, c.Name)
  299. }
  300. normalKey := strings.Join(normalJoin, ",")
  301. if normalIndexSet.Contains(normalKey) {
  302. log.Warning("table %s: duplicate index, %s", table.Table, normalKey)
  303. continue
  304. }
  305. normalIndexSet.AddStr(normalKey)
  306. sort.Slice(list, func(i, j int) bool {
  307. return list[i].SeqInIndex < list[j].SeqInIndex
  308. })
  309. reply.NormalIndex[indexName] = list
  310. }
  311. return &reply, nil
  312. }
  313. func getTableFields(table *model.Table) (map[string]*Field, error) {
  314. fieldM := make(map[string]*Field)
  315. for _, each := range table.Columns {
  316. isDefaultNull := each.ColumnDefault == nil && each.IsNullAble == "YES"
  317. dt, err := converter.ConvertDataType(each.DataType, isDefaultNull)
  318. if err != nil {
  319. return nil, err
  320. }
  321. columnSeqInIndex := 0
  322. if each.Index != nil {
  323. columnSeqInIndex = each.Index.SeqInIndex
  324. }
  325. field := &Field{
  326. Name: stringx.From(each.Name),
  327. DataBaseType: each.DataType,
  328. DataType: dt,
  329. Comment: each.Comment,
  330. SeqInIndex: columnSeqInIndex,
  331. OrdinalPosition: each.OrdinalPosition,
  332. }
  333. fieldM[each.Name] = field
  334. }
  335. return fieldM, nil
  336. }