parser.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. package parser
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/tal-tech/go-zero/tools/goctl/model/sql/converter"
  6. "github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
  7. "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
  8. "github.com/xwb1989/sqlparser"
  9. )
  10. const (
  11. _ = iota
  12. primary
  13. unique
  14. normal
  15. spatial
  16. )
  17. const timeImport = "time.Time"
  18. type (
  19. // Table describes a mysql table
  20. Table struct {
  21. Name stringx.String
  22. PrimaryKey Primary
  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. Name stringx.String
  33. DataBaseType string
  34. DataType string
  35. IsPrimaryKey bool
  36. IsUniqueKey bool
  37. Comment string
  38. }
  39. // KeyType types alias of int
  40. KeyType int
  41. )
  42. // Parse parses ddl into golang structure
  43. func Parse(ddl string) (*Table, error) {
  44. stmt, err := sqlparser.ParseStrictDDL(ddl)
  45. if err != nil {
  46. return nil, err
  47. }
  48. ddlStmt, ok := stmt.(*sqlparser.DDL)
  49. if !ok {
  50. return nil, errUnsupportDDL
  51. }
  52. action := ddlStmt.Action
  53. if action != sqlparser.CreateStr {
  54. return nil, fmt.Errorf("expected [CREATE] action,but found: %s", action)
  55. }
  56. tableName := ddlStmt.NewName.Name.String()
  57. tableSpec := ddlStmt.TableSpec
  58. if tableSpec == nil {
  59. return nil, errTableBodyNotFound
  60. }
  61. columns := tableSpec.Columns
  62. indexes := tableSpec.Indexes
  63. keyMap, err := getIndexKeyType(indexes)
  64. if err != nil {
  65. return nil, err
  66. }
  67. fields, primaryKey, err := convertFileds(columns, keyMap)
  68. if err != nil {
  69. return nil, err
  70. }
  71. return &Table{
  72. Name: stringx.From(tableName),
  73. PrimaryKey: primaryKey,
  74. Fields: fields,
  75. }, nil
  76. }
  77. func convertFileds(columns []*sqlparser.ColumnDefinition, keyMap map[string]KeyType) ([]Field, Primary, error) {
  78. var fields []Field
  79. var primaryKey Primary
  80. for _, column := range columns {
  81. if column == nil {
  82. continue
  83. }
  84. var comment string
  85. if column.Type.Comment != nil {
  86. comment = string(column.Type.Comment.Val)
  87. }
  88. var isDefaultNull = true
  89. if column.Type.NotNull {
  90. isDefaultNull = false
  91. } else {
  92. if column.Type.Default == nil {
  93. isDefaultNull = false
  94. } else if string(column.Type.Default.Val) != "null" {
  95. isDefaultNull = false
  96. }
  97. }
  98. dataType, err := converter.ConvertDataType(column.Type.Type, isDefaultNull)
  99. if err != nil {
  100. return nil, primaryKey, err
  101. }
  102. var field Field
  103. field.Name = stringx.From(column.Name.String())
  104. field.DataBaseType = column.Type.Type
  105. field.DataType = dataType
  106. field.Comment = comment
  107. key, ok := keyMap[column.Name.String()]
  108. if ok {
  109. field.IsPrimaryKey = key == primary
  110. field.IsUniqueKey = key == unique
  111. if field.IsPrimaryKey {
  112. primaryKey.Field = field
  113. if column.Type.Autoincrement {
  114. primaryKey.AutoIncrement = true
  115. }
  116. }
  117. }
  118. fields = append(fields, field)
  119. }
  120. return fields, primaryKey, nil
  121. }
  122. func getIndexKeyType(indexes []*sqlparser.IndexDefinition) (map[string]KeyType, error) {
  123. keyMap := make(map[string]KeyType)
  124. for _, index := range indexes {
  125. info := index.Info
  126. if info == nil {
  127. continue
  128. }
  129. if info.Primary {
  130. if len(index.Columns) > 1 {
  131. return nil, errPrimaryKey
  132. }
  133. keyMap[index.Columns[0].Column.String()] = primary
  134. continue
  135. }
  136. // can optimize
  137. if len(index.Columns) > 1 {
  138. continue
  139. }
  140. column := index.Columns[0]
  141. columnName := column.Column.String()
  142. camelColumnName := stringx.From(columnName).ToCamel()
  143. // by default, createTime|updateTime findOne is not used.
  144. if camelColumnName == "CreateTime" || camelColumnName == "UpdateTime" {
  145. continue
  146. }
  147. if info.Unique {
  148. keyMap[columnName] = unique
  149. } else if info.Spatial {
  150. keyMap[columnName] = spatial
  151. } else {
  152. keyMap[columnName] = normal
  153. }
  154. }
  155. return keyMap, nil
  156. }
  157. // ContainsTime determines whether the table field contains time.Time
  158. func (t *Table) ContainsTime() bool {
  159. for _, item := range t.Fields {
  160. if item.DataType == timeImport {
  161. return true
  162. }
  163. }
  164. return false
  165. }
  166. // ConvertColumn provides type conversion for mysql clolumn, primary key lookup
  167. func ConvertColumn(db, table string, in []*model.Column) (*Table, error) {
  168. var reply Table
  169. reply.Name = stringx.From(table)
  170. keyMap := make(map[string][]*model.Column)
  171. for _, column := range in {
  172. keyMap[column.Key] = append(keyMap[column.Key], column)
  173. }
  174. primaryColumns := keyMap["PRI"]
  175. if len(primaryColumns) == 0 {
  176. return nil, fmt.Errorf("database:%s, table %s: missing primary key", db, table)
  177. }
  178. if len(primaryColumns) > 1 {
  179. return nil, fmt.Errorf("database:%s, table %s: only one primary key expected", db, table)
  180. }
  181. primaryColumn := primaryColumns[0]
  182. isDefaultNull := primaryColumn.ColumnDefault == nil && primaryColumn.IsNullAble == "YES"
  183. primaryFt, err := converter.ConvertDataType(primaryColumn.DataType, isDefaultNull)
  184. if err != nil {
  185. return nil, err
  186. }
  187. primaryField := Field{
  188. Name: stringx.From(primaryColumn.Name),
  189. DataBaseType: primaryColumn.DataType,
  190. DataType: primaryFt,
  191. IsUniqueKey: true,
  192. IsPrimaryKey: true,
  193. Comment: primaryColumn.Comment,
  194. }
  195. reply.PrimaryKey = Primary{
  196. Field: primaryField,
  197. AutoIncrement: strings.Contains(primaryColumn.Extra, "auto_increment"),
  198. }
  199. for key, columns := range keyMap {
  200. for _, item := range columns {
  201. isColumnDefaultNull := item.ColumnDefault == nil && item.IsNullAble == "YES"
  202. dt, err := converter.ConvertDataType(item.DataType, isColumnDefaultNull)
  203. if err != nil {
  204. return nil, err
  205. }
  206. f := Field{
  207. Name: stringx.From(item.Name),
  208. DataBaseType: item.DataType,
  209. DataType: dt,
  210. IsPrimaryKey: primaryColumn.Name == item.Name,
  211. Comment: item.Comment,
  212. }
  213. if key == "UNI" {
  214. f.IsUniqueKey = true
  215. }
  216. reply.Fields = append(reply.Fields, f)
  217. }
  218. }
  219. return &reply, nil
  220. }