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 }