github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/cmd/internal/ddl/table/table.go (about)

     1  package table
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/iancoleman/strcase"
     6  	"github.com/pkg/errors"
     7  	"github.com/unionj-cloud/go-doudou/cmd/internal/astutils"
     8  	"github.com/unionj-cloud/go-doudou/cmd/internal/ddl/columnenum"
     9  	"github.com/unionj-cloud/go-doudou/cmd/internal/ddl/extraenum"
    10  	"github.com/unionj-cloud/go-doudou/cmd/internal/ddl/keyenum"
    11  	"github.com/unionj-cloud/go-doudou/cmd/internal/ddl/nullenum"
    12  	"github.com/unionj-cloud/go-doudou/cmd/internal/ddl/sortenum"
    13  	"github.com/unionj-cloud/go-doudou/toolkit/stringutils"
    14  	"github.com/unionj-cloud/go-doudou/toolkit/templateutils"
    15  	"regexp"
    16  	"sort"
    17  	"strconv"
    18  	"strings"
    19  )
    20  
    21  const (
    22  	now = "CURRENT_TIMESTAMP"
    23  )
    24  
    25  // IndexItems slice type alias for IndexItem
    26  type IndexItems []IndexItem
    27  
    28  // IndexItem define an index item
    29  type IndexItem struct {
    30  	Unique bool
    31  	Name   string
    32  	Column string
    33  	Order  int
    34  	Sort   sortenum.Sort
    35  }
    36  
    37  // Len return length of IndexItems
    38  func (it IndexItems) Len() int {
    39  	return len(it)
    40  }
    41  
    42  // Less define asc or desc order
    43  func (it IndexItems) Less(i, j int) bool {
    44  	return it[i].Order < it[j].Order
    45  }
    46  
    47  // Swap change position of elements at i and j
    48  func (it IndexItems) Swap(i, j int) {
    49  	it[i], it[j] = it[j], it[i]
    50  }
    51  
    52  // Index define an index
    53  type Index struct {
    54  	Table  string
    55  	Unique bool
    56  	Name   string
    57  	Items  []IndexItem
    58  }
    59  
    60  const indexsqltmpl = `{{define "drop"}}
    61  ALTER TABLE ` + "`" + `{{.Table}}` + "`" + ` DROP INDEX ` + "`" + `{{.Name}}` + "`" + `;
    62  {{end}}
    63  
    64  {{define "add"}}
    65  ALTER TABLE ` + "`" + `{{.Table}}` + "`" + ` ADD {{if .Unique}}UNIQUE{{end}} INDEX ` + "`" + `{{.Name}}` + "`" + ` ({{range $j, $it := .Items}}{{if $j}},{{end}}` + "`" + `{{$it.Column}}` + "`" + ` {{$it.Sort}}{{end}});
    66  {{end}}`
    67  
    68  func (idx *Index) DropIndexSql() (string, error) {
    69  	return templateutils.StringBlock("index.tmpl", indexsqltmpl, "drop", idx)
    70  }
    71  
    72  func (idx *Index) AddIndexSql() (string, error) {
    73  	return templateutils.StringBlock("index.tmpl", indexsqltmpl, "add", idx)
    74  }
    75  
    76  func NewIndexFromDbIndexes(dbIndexes []DbIndex) Index {
    77  	unique := !dbIndexes[0].NonUnique
    78  	idxName := dbIndexes[0].KeyName
    79  	items := make([]IndexItem, len(dbIndexes))
    80  	for i, idx := range dbIndexes {
    81  		var sor sortenum.Sort
    82  		if idx.Collation == "B" {
    83  			sor = sortenum.Desc
    84  		} else {
    85  			sor = sortenum.Asc
    86  		}
    87  		items[i] = IndexItem{
    88  			Column: idx.ColumnName,
    89  			Order:  idx.SeqInIndex,
    90  			Sort:   sor,
    91  		}
    92  	}
    93  	it := IndexItems(items)
    94  	sort.Stable(it)
    95  	return Index{
    96  		Unique: unique,
    97  		Name:   idxName,
    98  		Items:  it,
    99  	}
   100  }
   101  
   102  func toColumnType(goType string) columnenum.ColumnType {
   103  	switch goType {
   104  	case "int", "int16", "int32":
   105  		return columnenum.IntType
   106  	case "int64":
   107  		return columnenum.BigintType
   108  	case "float32":
   109  		return columnenum.FloatType
   110  	case "float64":
   111  		return columnenum.DoubleType
   112  	case "string":
   113  		return columnenum.VarcharType
   114  	case "bool", "int8":
   115  		return columnenum.TinyintType
   116  	case "time.Time":
   117  		return columnenum.DatetimeType
   118  	case "decimal.Decimal":
   119  		return "decimal(6,2)"
   120  	case "types.JSONText":
   121  		return columnenum.JSONType
   122  	}
   123  	panic(fmt.Sprintf("no available type %s", goType))
   124  }
   125  
   126  func toGoType(colType columnenum.ColumnType, nullable bool) string {
   127  	var goType string
   128  	if nullable {
   129  		goType += "*"
   130  	}
   131  	if stringutils.HasPrefixI(string(colType), strings.ToLower(string(columnenum.IntType))) {
   132  		goType += "int"
   133  	} else if stringutils.HasPrefixI(string(colType), strings.ToLower(string(columnenum.BigintType))) {
   134  		goType += "int64"
   135  	} else if stringutils.HasPrefixI(string(colType), strings.ToLower(string(columnenum.FloatType))) {
   136  		goType += "float32"
   137  	} else if stringutils.HasPrefixI(string(colType), strings.ToLower(string(columnenum.DoubleType))) {
   138  		goType += "float64"
   139  	} else if stringutils.HasPrefixI(string(colType), "varchar") {
   140  		goType += "string"
   141  	} else if stringutils.HasPrefixI(string(colType), strings.ToLower(string(columnenum.TextType))) {
   142  		goType += "string"
   143  	} else if stringutils.HasPrefixI(string(colType), strings.ToLower(string(columnenum.TinyintType))) {
   144  		goType += "int8"
   145  	} else if stringutils.HasPrefixI(string(colType), strings.ToLower(string(columnenum.DatetimeType))) {
   146  		goType += "time.Time"
   147  	} else if stringutils.HasPrefixI(string(colType), strings.ToLower(string(columnenum.MediumtextType))) {
   148  		goType += "string"
   149  	} else if stringutils.HasPrefixI(string(colType), strings.ToLower(string(columnenum.DecimalType))) {
   150  		goType += "decimal.Decimal"
   151  	} else if stringutils.HasPrefixI(string(colType), strings.ToLower(string(columnenum.LongtextType))) {
   152  		goType += "string"
   153  	} else if stringutils.HasPrefixI(string(colType), strings.ToLower(string(columnenum.JSONType))) {
   154  		goType += "types.JSONText"
   155  	} else {
   156  		panic(fmt.Sprintf("no available type %s", colType))
   157  	}
   158  	return goType
   159  }
   160  
   161  // CheckPk check key is primary key or not
   162  func CheckPk(key keyenum.Key) bool {
   163  	return key == keyenum.Pri
   164  }
   165  
   166  // CheckNull check a column is nullable or not
   167  func CheckNull(null nullenum.Null) bool {
   168  	return null == nullenum.Yes
   169  }
   170  
   171  // CheckUnsigned check a column is unsigned or not
   172  func CheckUnsigned(dbColType string) bool {
   173  	splits := strings.Split(dbColType, " ")
   174  	if len(splits) == 1 {
   175  		return false
   176  	}
   177  	return splits[1] == "unsigned"
   178  }
   179  
   180  // CheckAutoincrement check a column is auto increment or not
   181  func CheckAutoincrement(extra string) bool {
   182  	return strings.Contains(extra, "auto_increment")
   183  }
   184  
   185  // CheckAutoSet check a column is auto generated by database or not
   186  func CheckAutoSet(defaultVal string) bool {
   187  	return strings.ToLower(defaultVal) == strings.ToLower(now)
   188  }
   189  
   190  // Column define a column
   191  type Column struct {
   192  	Table         string
   193  	Name          string
   194  	Type          columnenum.ColumnType
   195  	Default       string
   196  	Pk            bool
   197  	Nullable      bool
   198  	Unsigned      bool
   199  	Autoincrement bool
   200  	Extra         extraenum.Extra
   201  	Meta          astutils.FieldMeta
   202  	AutoSet       bool
   203  	Indexes       []IndexItem
   204  	Fk            ForeignKey
   205  }
   206  
   207  var altersqltmpl = `{{define "change"}}
   208  ALTER TABLE ` + "`" + `{{.Table}}` + "`" + `
   209  CHANGE COLUMN ` + "`" + `{{.Name}}` + "`" + ` ` + "`" + `{{.Name}}` + "`" + ` {{.Type}} {{if .Nullable}}NULL{{else}}NOT NULL{{end}}{{if .Autoincrement}} AUTO_INCREMENT{{end}}{{if .Default}} DEFAULT {{.Default}}{{end}}{{if .Extra}} {{.Extra}}{{end}};
   210  {{end}}
   211  
   212  {{define "add"}}
   213  ALTER TABLE ` + "`" + `{{.Table}}` + "`" + `
   214  ADD COLUMN ` + "`" + `{{.Name}}` + "`" + ` {{.Type}} {{if .Nullable}}NULL{{else}}NOT NULL{{end}}{{if .Autoincrement}} AUTO_INCREMENT{{end}}{{if .Default}} DEFAULT {{.Default}}{{end}}{{if .Extra}} {{.Extra}}{{end}};
   215  {{end}}
   216  `
   217  
   218  // ChangeColumnSql return change column sql
   219  func (c *Column) ChangeColumnSql() (string, error) {
   220  	return templateutils.StringBlock("alter.tmpl", altersqltmpl, "change", c)
   221  }
   222  
   223  // AddColumnSql return add column sql
   224  func (c *Column) AddColumnSql() (string, error) {
   225  	return templateutils.StringBlock("alter.tmpl", altersqltmpl, "add", c)
   226  }
   227  
   228  // DbColumn defines a column
   229  type DbColumn struct {
   230  	Field   string        `db:"Field"`
   231  	Type    string        `db:"Type"`
   232  	Null    nullenum.Null `db:"Null"`
   233  	Key     keyenum.Key   `db:"Key"`
   234  	Default *string       `db:"Default"`
   235  	Extra   string        `db:"Extra"`
   236  	Comment string        `db:"Comment"`
   237  }
   238  
   239  // DbIndex defines an index refer to https://www.mysqltutorial.org/mysql-index/mysql-show-indexes/
   240  type DbIndex struct {
   241  	Table      string `db:"Table"`        // The name of the table
   242  	NonUnique  bool   `db:"Non_unique"`   // 1 if the index can contain duplicates, 0 if it cannot.
   243  	KeyName    string `db:"Key_name"`     // The name of the index. The primary key index always has the name of PRIMARY.
   244  	SeqInIndex int    `db:"Seq_in_index"` // The column sequence number in the index. The first column sequence number starts from 1.
   245  	ColumnName string `db:"Column_name"`  // The column name
   246  	Collation  string `db:"Collation"`    // Collation represents how the column is sorted in the index. A means ascending, B means descending, or NULL means not sorted.
   247  }
   248  
   249  // DbForeignKey from INFORMATION_SCHEMA.KEY_COLUMN_USAGE
   250  type DbForeignKey struct {
   251  	TableName            string `db:"TABLE_NAME"`
   252  	ColumnName           string `db:"COLUMN_NAME"`
   253  	ConstraintName       string `db:"CONSTRAINT_NAME"`
   254  	ReferencedTableName  string `db:"REFERENCED_TABLE_NAME"`
   255  	ReferencedColumnName string `db:"REFERENCED_COLUMN_NAME"`
   256  }
   257  
   258  // DbAction from information_schema.REFERENTIAL_CONSTRAINTS
   259  type DbAction struct {
   260  	TableName           string `db:"TABLE_NAME"`
   261  	ConstraintName      string `db:"CONSTRAINT_NAME"`
   262  	ReferencedTableName string `db:"REFERENCED_TABLE_NAME"`
   263  	UpdateRule          string `db:"UPDATE_RULE"`
   264  	DeleteRule          string `db:"DELETE_RULE"`
   265  }
   266  
   267  type ForeignKey struct {
   268  	// Table the child table
   269  	Table string
   270  	// Constraint name of foreign key constraint
   271  	Constraint string
   272  	// Fk foreign key
   273  	Fk string
   274  	// ReferencedTable the referenced table
   275  	ReferencedTable string
   276  	// ReferencedCol the referenced column of ReferencedTable
   277  	ReferencedCol string
   278  	UpdateRule    string
   279  	DeleteRule    string
   280  	FullRule      string
   281  }
   282  
   283  const fksqltmpl = `{{define "drop"}}
   284  ALTER TABLE ` + "`" + `{{.Table}}` + "`" + ` DROP FOREIGN KEY {{.Constraint}};
   285  {{end}}
   286  
   287  {{define "add"}}
   288  ALTER TABLE ` + "`" + `{{.Table}}` + "`" + ` ADD CONSTRAINT {{.Constraint}} FOREIGN KEY ({{.Fk}}) REFERENCES {{.ReferencedTable}}({{.ReferencedCol}}) {{.FullRule}};
   289  {{end}}`
   290  
   291  func (fk *ForeignKey) DropFkSql() (string, error) {
   292  	return templateutils.StringBlock("fk.tmpl", fksqltmpl, "drop", fk)
   293  }
   294  
   295  func (fk *ForeignKey) AddFkSql() (string, error) {
   296  	return templateutils.StringBlock("fk.tmpl", fksqltmpl, "add", fk)
   297  }
   298  
   299  // Table defines a table
   300  type Table struct {
   301  	Name    string
   302  	Columns []Column
   303  	Pk      string
   304  	Indexes []Index
   305  	Meta    astutils.StructMeta
   306  	Fks     []ForeignKey
   307  }
   308  
   309  // NewTableFromStruct creates a Table instance from structMeta
   310  func NewTableFromStruct(structMeta astutils.StructMeta, prefix ...string) Table {
   311  	var (
   312  		columns       []Column
   313  		uniqueindexes []Index
   314  		indexes       []Index
   315  		fks           []ForeignKey
   316  		pkColumn      Column
   317  		table         string
   318  	)
   319  	table = strcase.ToSnake(structMeta.Name)
   320  	if len(prefix) > 0 {
   321  		table = prefix[0] + table
   322  	}
   323  	for _, field := range structMeta.Fields {
   324  		var (
   325  			columnName     string
   326  			_uniqueindexes []Index
   327  			_indexes       []Index
   328  			_fks           []ForeignKey
   329  			column         Column
   330  		)
   331  		column.Table = table
   332  		column.Meta = field
   333  		columnName = strcase.ToSnake(field.Name)
   334  		column.Name = columnName
   335  		if stringutils.IsNotEmpty(field.Tag) {
   336  			tags := strings.Split(field.Tag, `" `)
   337  			var ddTag string
   338  			for _, tag := range tags {
   339  				if strings.HasPrefix(tag, "dd:") {
   340  					ddTag = strings.Trim(strings.TrimPrefix(tag, "dd:"), `"`)
   341  					break
   342  				}
   343  			}
   344  			if stringutils.IsNotEmpty(ddTag) {
   345  				_indexes, _uniqueindexes, _fks = parseDdTag(ddTag, field, &column)
   346  			}
   347  		}
   348  
   349  		if strings.HasPrefix(field.Type, "*") {
   350  			column.Nullable = true
   351  		}
   352  
   353  		if stringutils.IsEmpty(string(column.Type)) {
   354  			column.Type = toColumnType(strings.TrimPrefix(field.Type, "*"))
   355  		}
   356  
   357  		for _, idx := range _indexes {
   358  			if stringutils.IsNotEmpty(idx.Name) {
   359  				idx.Items[0].Column = columnName
   360  				indexes = append(indexes, idx)
   361  			}
   362  		}
   363  
   364  		for _, uidx := range _uniqueindexes {
   365  			if stringutils.IsNotEmpty(uidx.Name) {
   366  				uidx.Items[0].Column = columnName
   367  				uniqueindexes = append(uniqueindexes, uidx)
   368  			}
   369  		}
   370  
   371  		for _, fk := range _fks {
   372  			if stringutils.IsNotEmpty(fk.Fk) {
   373  				fks = append(fks, fk)
   374  			}
   375  		}
   376  
   377  		columns = append(columns, column)
   378  	}
   379  
   380  	for _, column := range columns {
   381  		if column.Pk {
   382  			pkColumn = column
   383  			break
   384  		}
   385  	}
   386  
   387  	indexesResult := mergeIndexes(indexes, uniqueindexes)
   388  
   389  	return Table{
   390  		Name:    table,
   391  		Columns: columns,
   392  		Pk:      pkColumn.Name,
   393  		Indexes: indexesResult,
   394  		Meta:    structMeta,
   395  		Fks:     fks,
   396  	}
   397  }
   398  
   399  type sortableIndexes []Index
   400  
   401  // Len return length of sortableIndexes
   402  func (it sortableIndexes) Len() int {
   403  	return len(it)
   404  }
   405  
   406  // Less define asc or desc order
   407  func (it sortableIndexes) Less(i, j int) bool {
   408  	return it[i].Name < it[j].Name
   409  }
   410  
   411  // Swap change position of elements at i and j
   412  func (it sortableIndexes) Swap(i, j int) {
   413  	it[i], it[j] = it[j], it[i]
   414  }
   415  
   416  func mergeIndexes(indexes, uniqueindexes []Index) []Index {
   417  	uniqueMap := make(map[string][]IndexItem)
   418  	indexMap := make(map[string][]IndexItem)
   419  
   420  	for _, unique := range uniqueindexes {
   421  		if items, exists := uniqueMap[unique.Name]; exists {
   422  			items = append(items, unique.Items...)
   423  			uniqueMap[unique.Name] = items
   424  		} else {
   425  			uniqueMap[unique.Name] = unique.Items
   426  		}
   427  	}
   428  
   429  	for _, index := range indexes {
   430  		if items, exists := indexMap[index.Name]; exists {
   431  			items = append(items, index.Items...)
   432  			indexMap[index.Name] = items
   433  		} else {
   434  			indexMap[index.Name] = index.Items
   435  		}
   436  	}
   437  
   438  	var uniquesResult, indexesResult []Index
   439  
   440  	for k, v := range uniqueMap {
   441  		it := IndexItems(v)
   442  		sort.Stable(it)
   443  		uniquesResult = append(uniquesResult, Index{
   444  			Unique: true,
   445  			Name:   k,
   446  			Items:  it,
   447  		})
   448  	}
   449  
   450  	for k, v := range indexMap {
   451  		it := IndexItems(v)
   452  		sort.Stable(it)
   453  		indexesResult = append(indexesResult, Index{
   454  			Name:  k,
   455  			Items: it,
   456  		})
   457  	}
   458  
   459  	sort.Stable(sortableIndexes(indexesResult))
   460  	sort.Stable(sortableIndexes(uniquesResult))
   461  
   462  	indexesResult = append(indexesResult, uniquesResult...)
   463  	return indexesResult
   464  }
   465  
   466  func parseDdTag(ddTag string, field astutils.FieldMeta, column *Column) (indexes []Index, uniqueIndexes []Index, fks []ForeignKey) {
   467  	kvs := strings.Split(ddTag, ";")
   468  	for _, kv := range kvs {
   469  		pair := strings.Split(kv, ":")
   470  		if len(pair) > 1 {
   471  			parsePair(pair, column, &indexes, &uniqueIndexes, &fks)
   472  		} else {
   473  			parseSingle(pair, column, field, &indexes, &uniqueIndexes)
   474  		}
   475  	}
   476  	return
   477  }
   478  
   479  func parseSingle(pair []string, column *Column, field astutils.FieldMeta, indexes *[]Index, uniqueIndexes *[]Index) {
   480  	key := pair[0]
   481  	switch key {
   482  	case "pk":
   483  		column.Pk = true
   484  		break
   485  	case "null":
   486  		column.Nullable = true
   487  		break
   488  	case "unsigned":
   489  		column.Unsigned = true
   490  		break
   491  	case "auto":
   492  		column.Autoincrement = true
   493  		break
   494  	case "index":
   495  		*indexes = append(*indexes, Index{
   496  			Name: strcase.ToSnake(field.Name) + "_idx",
   497  			Items: []IndexItem{
   498  				{
   499  					Order: 1,
   500  					Sort:  sortenum.Asc,
   501  				},
   502  			},
   503  		})
   504  		break
   505  	case "unique":
   506  		*uniqueIndexes = append(*uniqueIndexes, Index{
   507  			Name: strcase.ToSnake(field.Name) + "_idx",
   508  			Items: []IndexItem{
   509  				{
   510  					Order: 1,
   511  					Sort:  sortenum.Asc,
   512  				},
   513  			},
   514  		})
   515  		break
   516  	}
   517  }
   518  
   519  func parsePair(pair []string, column *Column, indexes *[]Index, uniqueIndexes *[]Index, fks *[]ForeignKey) {
   520  	key := pair[0]
   521  	value := pair[1]
   522  	if stringutils.IsEmpty(value) {
   523  		panic(fmt.Sprintf("%+v", errors.New("value should not be empty")))
   524  	}
   525  	switch key {
   526  	case "type":
   527  		column.Type = columnenum.ColumnType(value)
   528  		break
   529  	case "default":
   530  		column.Default = value
   531  		column.AutoSet = CheckAutoSet(value)
   532  		break
   533  	case "extra":
   534  		column.Extra = extraenum.Extra(value)
   535  		break
   536  	case "index":
   537  		props := strings.Split(value, ",")
   538  		indexName := props[0]
   539  		order := props[1]
   540  		orderInt, err := strconv.Atoi(order)
   541  		if err != nil {
   542  			panic(err)
   543  		}
   544  		var sor sortenum.Sort
   545  		if len(props) < 3 || stringutils.IsEmpty(props[2]) {
   546  			sor = sortenum.Asc
   547  		} else {
   548  			sor = sortenum.Sort(props[2])
   549  		}
   550  		*indexes = append(*indexes, Index{
   551  			Name: indexName,
   552  			Items: []IndexItem{
   553  				{
   554  					Order: orderInt,
   555  					Sort:  sor,
   556  				},
   557  			},
   558  		})
   559  		break
   560  	case "unique":
   561  		props := strings.Split(value, ",")
   562  		indexName := props[0]
   563  		order := props[1]
   564  		orderInt, err := strconv.Atoi(order)
   565  		if err != nil {
   566  			panic(err)
   567  		}
   568  		var sort sortenum.Sort
   569  		if len(props) < 3 || stringutils.IsEmpty(props[2]) {
   570  			sort = sortenum.Asc
   571  		} else {
   572  			sort = sortenum.Sort(props[2])
   573  		}
   574  		*uniqueIndexes = append(*uniqueIndexes, Index{
   575  			Name: indexName,
   576  			Items: []IndexItem{
   577  				{
   578  					Order: orderInt,
   579  					Sort:  sort,
   580  				},
   581  			},
   582  		})
   583  		break
   584  	case "fk":
   585  		props := strings.Split(value, ",")
   586  		refTable := props[0]
   587  		var refCol string
   588  		if len(props) > 1 {
   589  			refCol = props[1]
   590  		} else {
   591  			refCol = "id"
   592  		}
   593  		var constraint string
   594  		if len(props) > 2 {
   595  			constraint = props[2]
   596  		} else {
   597  			constraint = fmt.Sprintf("fk_%s_%s_%s", column.Name, refTable, refCol)
   598  		}
   599  		var fullRule string
   600  		if len(props) > 3 {
   601  			fullRule = props[3]
   602  		}
   603  		*fks = append(*fks, ForeignKey{
   604  			Table:           column.Table,
   605  			Constraint:      constraint,
   606  			Fk:              column.Name,
   607  			ReferencedTable: refTable,
   608  			ReferencedCol:   refCol,
   609  			FullRule:        fullRule,
   610  		})
   611  		break
   612  	}
   613  }
   614  
   615  // NewFieldFromColumn creates an astutils.FieldMeta instance from col
   616  func NewFieldFromColumn(col Column) astutils.FieldMeta {
   617  	tag := "dd:"
   618  	var feats []string
   619  	if col.Pk {
   620  		feats = append(feats, "pk")
   621  	}
   622  	if col.Autoincrement {
   623  		feats = append(feats, "auto")
   624  	}
   625  	goType := toGoType(col.Type, col.Nullable)
   626  	if col.Nullable && !strings.HasPrefix(goType, "*") {
   627  		feats = append(feats, "null")
   628  	}
   629  	if stringutils.IsNotEmpty(string(col.Type)) {
   630  		feats = append(feats, fmt.Sprintf("type:%s", string(col.Type)))
   631  	}
   632  	if stringutils.IsNotEmpty(col.Default) {
   633  		val := col.Default
   634  		re := regexp.MustCompile(`^\(.+\)$`)
   635  		var defaultClause string
   636  		if strings.ToUpper(val) == "CURRENT_TIMESTAMP" || re.MatchString(val) {
   637  			defaultClause = fmt.Sprintf("default:%s", val)
   638  		} else {
   639  			defaultClause = fmt.Sprintf("default:'%s'", val)
   640  		}
   641  		feats = append(feats, defaultClause)
   642  	}
   643  	if stringutils.IsNotEmpty(string(col.Extra)) {
   644  		feats = append(feats, fmt.Sprintf("extra:%s", string(col.Extra)))
   645  	}
   646  	for _, idx := range col.Indexes {
   647  		var indexClause string
   648  		if idx.Name == "PRIMARY" {
   649  			continue
   650  		}
   651  		if idx.Unique {
   652  			indexClause = "unique:"
   653  		} else {
   654  			indexClause = "index:"
   655  		}
   656  		indexClause += fmt.Sprintf("%s,%d,%s", idx.Name, idx.Order, string(idx.Sort))
   657  		feats = append(feats, indexClause)
   658  	}
   659  	if stringutils.IsNotEmpty(col.Fk.Constraint) {
   660  		feats = append(feats, fmt.Sprintf("fk:%s,%s,%s,%s", col.Fk.ReferencedTable, col.Fk.ReferencedCol, col.Fk.Constraint, col.Fk.FullRule))
   661  	}
   662  	return astutils.FieldMeta{
   663  		Name: strcase.ToCamel(col.Name),
   664  		Type: goType,
   665  		Tag:  fmt.Sprintf(`%s"%s"`, tag, strings.Join(feats, ";")),
   666  	}
   667  }
   668  
   669  var createsqltmpl = `CREATE TABLE ` + "`" + `{{.Name}}` + "`" + ` (
   670  {{- range $co := .Columns }}
   671  ` + "`" + `{{$co.Name}}` + "`" + ` {{$co.Type}} {{if $co.Nullable}}NULL{{else}}NOT NULL{{end}}{{if $co.Autoincrement}} AUTO_INCREMENT{{end}}{{if $co.Default}} DEFAULT {{$co.Default}}{{end}}{{if $co.Extra}} {{$co.Extra}}{{end}},
   672  {{- end }}
   673  PRIMARY KEY (` + "`" + `{{.Pk}}` + "`" + `){{if .Indexes}},{{end}}
   674  {{- range $i, $ind := .Indexes}}
   675  {{- if $i}},{{end}}
   676  {{if $ind.Unique}}UNIQUE {{end}}INDEX ` + "`" + `{{$ind.Name}}` + "`" + ` ({{ range $j, $it := $ind.Items }}{{if $j}},{{end}}` + "`" + `{{$it.Column}}` + "`" + ` {{$it.Sort}}{{ end }})
   677  {{- end }}{{if .Fks}},{{end}}
   678  {{- range $i, $fk := .Fks}}
   679  {{- if $i}},{{end}}
   680  CONSTRAINT ` + "`" + `{{$fk.Constraint}}` + "`" + ` FOREIGN KEY (` + "`" + `{{$fk.Fk}}` + "`" + `)
   681  REFERENCES ` + "`" + `{{$fk.ReferencedTable}}` + "`" + `(` + "`" + `{{$fk.ReferencedCol}}` + "`" + `)
   682  {{- if $fk.FullRule}}
   683  {{$fk.FullRule}}
   684  {{- end }}
   685  {{- end }})`
   686  
   687  // CreateSql return create table sql
   688  func (t *Table) CreateSql() (string, error) {
   689  	return templateutils.String("create.sql.tmpl", createsqltmpl, t)
   690  }