github.com/artisanhe/tools@v1.0.1-0.20210607022958-19a8fef2eb04/sqlx/data_type/column_type.go (about)

     1  package data_type
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"regexp"
     7  	"strconv"
     8  	"strings"
     9  )
    10  
    11  var (
    12  	reColumnType = regexp.MustCompile("(\\w+)(\\((\\d+)(,(\\d+))?\\))?")
    13  	reDefault    = regexp.MustCompile("DEFAULT (('([^']+)?')|(\\w+)(\\((\\d+)(,(\\d+))?\\))?)")
    14  	reCharset    = regexp.MustCompile("CHARACTER SET (\\S+)")
    15  	reCollate    = regexp.MustCompile("COLLATE (\\S+)")
    16  )
    17  
    18  func pickDefault(s string) (sql string, defaultValue string, exists bool, err error) {
    19  	sql = s
    20  	values := reDefault.FindStringSubmatch(sql)
    21  	if len(values) == 9 {
    22  		exists = true
    23  		sql = reDefault.ReplaceAllString(sql, "")
    24  		if values[4] != "" {
    25  			defaultValue = values[4]
    26  			if !ValueKeywords[defaultValue] {
    27  				err = fmt.Errorf("unsupport default value %s", defaultValue)
    28  				return
    29  			}
    30  		} else {
    31  			defaultValue = values[3]
    32  		}
    33  	}
    34  	return
    35  }
    36  
    37  func ParseColumnType(sql string) (columnType *ColumnType, err error) {
    38  	columnType = &ColumnType{}
    39  
    40  	sql, columnType.Default, columnType.HasDefault, err = pickDefault(sql)
    41  	if err != nil {
    42  		return
    43  	}
    44  
    45  	dataTypeAndDesc := reColumnType.FindStringSubmatch(sql)
    46  	if len(dataTypeAndDesc) != 6 {
    47  		err = fmt.Errorf("missing sql data type")
    48  		return
    49  	}
    50  
    51  	columnType.DataType = DataType(strings.ToLower(dataTypeAndDesc[1]))
    52  	if dataTypeAndDesc[3] != "" {
    53  		columnType.Length, _ = strconv.ParseInt(dataTypeAndDesc[3], 10, 64)
    54  	}
    55  	if dataTypeAndDesc[5] != "" {
    56  		columnType.Decimal, _ = strconv.ParseInt(dataTypeAndDesc[5], 10, 64)
    57  	}
    58  
    59  	upperSQL := strings.ToUpper(sql)
    60  
    61  	columnType.NotNull = strings.Contains(upperSQL, "NOT NULL")
    62  
    63  	if columnType.IsInteger() || columnType.IsFloating() {
    64  		columnType.AutoIncrement = strings.Contains(upperSQL, "AUTO_INCREMENT")
    65  	}
    66  
    67  	if columnType.IsInteger() || columnType.IsFloating() || columnType.IsFixed() {
    68  		columnType.Zerofill = strings.Contains(upperSQL, "ZEROFILL")
    69  		columnType.Unsigned = strings.Contains(upperSQL, "UNSIGNED") || columnType.Zerofill
    70  	}
    71  
    72  	if columnType.IsChar() || columnType.IsText() {
    73  		charset := "utf8"
    74  		{
    75  			values := reCharset.FindStringSubmatch(upperSQL)
    76  			if len(values) == 2 {
    77  				columnType.Charset = strings.ToLower(values[1])
    78  				charset = columnType.Charset
    79  			}
    80  		}
    81  		if strings.Contains(upperSQL, "BINARY") {
    82  			columnType.Collate = charset + "_bin"
    83  		}
    84  		{
    85  			values := reCollate.FindStringSubmatch(upperSQL)
    86  			if len(values) == 2 {
    87  				columnType.Collate = strings.ToLower(values[1])
    88  			}
    89  		}
    90  	}
    91  
    92  	if columnType.IsDate() {
    93  		columnType.OnUpdateByCurrentTimestamp = strings.Contains(upperSQL, "ON UPDATE CURRENT_TIMESTAMP")
    94  	}
    95  
    96  	columnType.Zerofill = strings.Contains(upperSQL, "ZEROFILL")
    97  
    98  	return
    99  }
   100  
   101  type ColumnType struct {
   102  	// type
   103  	DataType
   104  	// length
   105  	Length int64
   106  	// 小数点位数
   107  	Decimal int64
   108  	// todo enum 值 or set
   109  	Values   []string
   110  	Unsigned bool
   111  	// [CHARACTER SET utf8 when text] COLLATE utf8_bin
   112  	Charset string
   113  	Collate string
   114  
   115  	Zerofill bool
   116  	NotNull  bool
   117  
   118  	EnumType string
   119  	Enums    map[int][]string
   120  
   121  	HasDefault bool
   122  	Default    string
   123  
   124  	// extra
   125  	AutoIncrement              bool
   126  	OnUpdateByCurrentTimestamp bool
   127  
   128  	Comment string
   129  }
   130  
   131  func (columnType ColumnType) IsEnum() bool {
   132  	return columnType.EnumType != "" && len(columnType.Enums) > 0
   133  }
   134  
   135  func (columnType ColumnType) DeAlias() *ColumnType {
   136  	if columnType.Charset == "" {
   137  		columnType.Charset = "utf8"
   138  	}
   139  
   140  	switch columnType.DataType.String() {
   141  	case "timestamp", "datetime":
   142  		if columnType.HasDefault && columnType.Default == "0" {
   143  			columnType.Default = "0000-00-00 00:00:00"
   144  			if columnType.Length > 0 {
   145  				columnType.Default += "." + strings.Repeat("0", int(columnType.Length))
   146  			}
   147  		}
   148  		return &columnType
   149  	case "boolean":
   150  		columnType.DataType = "tinyint"
   151  		columnType.Length = 1
   152  		return &columnType
   153  	default:
   154  		if columnType.Length == 0 {
   155  			switch columnType.DataType.String() {
   156  			case "tinyint":
   157  				columnType.Length = 3
   158  			case "smallint":
   159  				columnType.Length = 6
   160  			case "int":
   161  				columnType.Length = 11
   162  			case "bigint":
   163  				columnType.Length = 20
   164  			}
   165  		}
   166  		return &columnType
   167  	}
   168  }
   169  
   170  func (columnType *ColumnType) asDefaultValue() string {
   171  	if ValueKeywords[strings.ToUpper(columnType.Default)] {
   172  		if columnType.IsDate() && columnType.Length > 0 {
   173  			return fmt.Sprintf("%s(%d)", columnType.Default, columnType.Length)
   174  		}
   175  		return columnType.Default
   176  	}
   177  	return `'` + columnType.Default + `'`
   178  }
   179  
   180  func (columnType ColumnType) String() string {
   181  	b := bytes.Buffer{}
   182  	if len(columnType.Values) > 0 {
   183  		b.WriteString(columnType.DataType.WithArgs(columnType.Values...))
   184  	} else if columnType.Length > 0 {
   185  		if columnType.Decimal > 0 {
   186  			b.WriteString(columnType.DataType.WithArgs(
   187  				fmt.Sprintf("%d", columnType.Length),
   188  				fmt.Sprintf("%d", columnType.Decimal),
   189  			))
   190  		} else {
   191  			b.WriteString(columnType.DataType.WithArgs(
   192  				fmt.Sprintf("%d", columnType.Length),
   193  			))
   194  		}
   195  	} else {
   196  		b.WriteString(columnType.DataType.String())
   197  	}
   198  
   199  	if columnType.Unsigned {
   200  		b.WriteString(" unsigned")
   201  	}
   202  
   203  	if columnType.Zerofill {
   204  		b.WriteString(" zerofill")
   205  	}
   206  
   207  	{
   208  		charset := "utf8"
   209  
   210  		if columnType.Charset != "" {
   211  			b.WriteString(" CHARACTER SET " + columnType.Charset)
   212  			charset = columnType.Charset
   213  		}
   214  
   215  		if columnType.Collate != "" {
   216  			if columnType.Collate == charset+"_bin" {
   217  				b.WriteString(" binary")
   218  			} else {
   219  				b.WriteString(" COLLATE " + columnType.Collate)
   220  			}
   221  		}
   222  	}
   223  
   224  	if columnType.NotNull {
   225  		b.WriteString(" NOT NULL")
   226  	}
   227  
   228  	if columnType.AutoIncrement {
   229  		b.WriteString(" AUTO_INCREMENT")
   230  	} else {
   231  		if columnType.HasDefault {
   232  			b.WriteString(fmt.Sprintf(" DEFAULT %s", columnType.asDefaultValue()))
   233  		}
   234  		if columnType.IsDate() && columnType.OnUpdateByCurrentTimestamp {
   235  			if columnType.Length > 0 {
   236  				b.WriteString(fmt.Sprintf(" ON UPDATE CURRENT_TIMESTAMP(%d)", columnType.Length))
   237  			} else {
   238  				b.WriteString(" ON UPDATE CURRENT_TIMESTAMP")
   239  			}
   240  		}
   241  	}
   242  
   243  	if columnType.Comment != "" {
   244  		b.WriteString(fmt.Sprintf(" COMMENT '%s'", strings.Replace(columnType.Comment, "'", "\\'", -1)))
   245  	}
   246  
   247  	return b.String()
   248  }
   249  
   250  var ValueKeywords = map[string]bool{
   251  	"NULL":              true,
   252  	"CURRENT_TIMESTAMP": true,
   253  }