123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- package parser
- import (
- "fmt"
- "strings"
- "github.com/tal-tech/go-zero/tools/goctl/model/sql/converter"
- "github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
- "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
- "github.com/xwb1989/sqlparser"
- )
- const (
- _ = iota
- primary
- unique
- normal
- spatial
- )
- const timeImport = "time.Time"
- type (
- // Table describes a mysql table
- Table struct {
- Name stringx.String
- PrimaryKey Primary
- Fields []Field
- }
- // Primary describes a primary key
- Primary struct {
- Field
- AutoIncrement bool
- }
- // Field describes a table field
- Field struct {
- Name stringx.String
- DataBaseType string
- DataType string
- IsPrimaryKey bool
- IsUniqueKey bool
- Comment string
- }
- // KeyType types alias of int
- KeyType int
- )
- // Parse parses ddl into golang structure
- func Parse(ddl string) (*Table, error) {
- stmt, err := sqlparser.ParseStrictDDL(ddl)
- if err != nil {
- return nil, err
- }
- ddlStmt, ok := stmt.(*sqlparser.DDL)
- if !ok {
- return nil, errUnsupportDDL
- }
- action := ddlStmt.Action
- if action != sqlparser.CreateStr {
- return nil, fmt.Errorf("expected [CREATE] action,but found: %s", action)
- }
- tableName := ddlStmt.NewName.Name.String()
- tableSpec := ddlStmt.TableSpec
- if tableSpec == nil {
- return nil, errTableBodyNotFound
- }
- columns := tableSpec.Columns
- indexes := tableSpec.Indexes
- keyMap, err := getIndexKeyType(indexes)
- if err != nil {
- return nil, err
- }
- fields, primaryKey, err := convertFileds(columns, keyMap)
- if err != nil {
- return nil, err
- }
- return &Table{
- Name: stringx.From(tableName),
- PrimaryKey: primaryKey,
- Fields: fields,
- }, nil
- }
- func convertFileds(columns []*sqlparser.ColumnDefinition, keyMap map[string]KeyType) ([]Field, Primary, error) {
- var fields []Field
- var primaryKey Primary
- for _, column := range columns {
- if column == nil {
- continue
- }
- var comment string
- if column.Type.Comment != nil {
- comment = string(column.Type.Comment.Val)
- }
- var isDefaultNull = true
- if column.Type.NotNull {
- isDefaultNull = false
- } else {
- if column.Type.Default == nil {
- isDefaultNull = false
- } else if string(column.Type.Default.Val) != "null" {
- isDefaultNull = false
- }
- }
- dataType, err := converter.ConvertDataType(column.Type.Type, isDefaultNull)
- if err != nil {
- return nil, primaryKey, err
- }
- var field Field
- field.Name = stringx.From(column.Name.String())
- field.DataBaseType = column.Type.Type
- field.DataType = dataType
- field.Comment = comment
- key, ok := keyMap[column.Name.String()]
- if ok {
- field.IsPrimaryKey = key == primary
- field.IsUniqueKey = key == unique
- if field.IsPrimaryKey {
- primaryKey.Field = field
- if column.Type.Autoincrement {
- primaryKey.AutoIncrement = true
- }
- }
- }
- fields = append(fields, field)
- }
- return fields, primaryKey, nil
- }
- func getIndexKeyType(indexes []*sqlparser.IndexDefinition) (map[string]KeyType, error) {
- keyMap := make(map[string]KeyType)
- for _, index := range indexes {
- info := index.Info
- if info == nil {
- continue
- }
- if info.Primary {
- if len(index.Columns) > 1 {
- return nil, errPrimaryKey
- }
- keyMap[index.Columns[0].Column.String()] = primary
- continue
- }
- // can optimize
- if len(index.Columns) > 1 {
- continue
- }
- column := index.Columns[0]
- columnName := column.Column.String()
- camelColumnName := stringx.From(columnName).ToCamel()
- // by default, createTime|updateTime findOne is not used.
- if camelColumnName == "CreateTime" || camelColumnName == "UpdateTime" {
- continue
- }
- if info.Unique {
- keyMap[columnName] = unique
- } else if info.Spatial {
- keyMap[columnName] = spatial
- } else {
- keyMap[columnName] = normal
- }
- }
- return keyMap, nil
- }
- // ContainsTime determines whether the table field contains time.Time
- func (t *Table) ContainsTime() bool {
- for _, item := range t.Fields {
- if item.DataType == timeImport {
- return true
- }
- }
- return false
- }
- // ConvertColumn provides type conversion for mysql clolumn, primary key lookup
- func ConvertColumn(db, table string, in []*model.Column) (*Table, error) {
- var reply Table
- reply.Name = stringx.From(table)
- keyMap := make(map[string][]*model.Column)
- for _, column := range in {
- keyMap[column.Key] = append(keyMap[column.Key], column)
- }
- primaryColumns := keyMap["PRI"]
- if len(primaryColumns) == 0 {
- return nil, fmt.Errorf("database:%s, table %s: missing primary key", db, table)
- }
- if len(primaryColumns) > 1 {
- return nil, fmt.Errorf("database:%s, table %s: only one primary key expected", db, table)
- }
- primaryColumn := primaryColumns[0]
- isDefaultNull := primaryColumn.ColumnDefault == nil && primaryColumn.IsNullAble == "YES"
- primaryFt, err := converter.ConvertDataType(primaryColumn.DataType, isDefaultNull)
- if err != nil {
- return nil, err
- }
- primaryField := Field{
- Name: stringx.From(primaryColumn.Name),
- DataBaseType: primaryColumn.DataType,
- DataType: primaryFt,
- IsUniqueKey: true,
- IsPrimaryKey: true,
- Comment: primaryColumn.Comment,
- }
- reply.PrimaryKey = Primary{
- Field: primaryField,
- AutoIncrement: strings.Contains(primaryColumn.Extra, "auto_increment"),
- }
- for key, columns := range keyMap {
- for _, item := range columns {
- isColumnDefaultNull := item.ColumnDefault == nil && item.IsNullAble == "YES"
- dt, err := converter.ConvertDataType(item.DataType, isColumnDefaultNull)
- if err != nil {
- return nil, err
- }
- f := Field{
- Name: stringx.From(item.Name),
- DataBaseType: item.DataType,
- DataType: dt,
- IsPrimaryKey: primaryColumn.Name == item.Name,
- Comment: item.Comment,
- }
- if key == "UNI" {
- f.IsUniqueKey = true
- }
- reply.Fields = append(reply.Fields, f)
- }
- }
- return &reply, nil
- }
|