github.com/pingcap/tidb/parser@v0.0.0-20231013125129-93a834a6bf8d/types/field_type_test.go (about) 1 // Copyright 2019 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package types_test 15 16 import ( 17 "fmt" 18 "testing" 19 20 "github.com/pingcap/tidb/parser" 21 "github.com/pingcap/tidb/parser/ast" 22 "github.com/pingcap/tidb/parser/charset" 23 "github.com/pingcap/tidb/parser/mysql" 24 // import parser_driver 25 _ "github.com/pingcap/tidb/parser/test_driver" 26 . "github.com/pingcap/tidb/parser/types" 27 "github.com/stretchr/testify/require" 28 ) 29 30 func TestFieldType(t *testing.T) { 31 ft := NewFieldType(mysql.TypeDuration) 32 require.Equal(t, UnspecifiedLength, ft.GetFlen()) 33 require.Equal(t, UnspecifiedLength, ft.GetDecimal()) 34 ft.SetDecimal(5) 35 require.Equal(t, "time(5)", ft.String()) 36 require.False(t, HasCharset(ft)) 37 38 ft = NewFieldType(mysql.TypeLong) 39 ft.SetFlen(5) 40 ft.SetFlag(mysql.UnsignedFlag | mysql.ZerofillFlag) 41 require.Equal(t, "int(5) UNSIGNED ZEROFILL", ft.String()) 42 require.Equal(t, "int(5) unsigned", ft.InfoSchemaStr()) 43 require.False(t, HasCharset(ft)) 44 45 ft = NewFieldType(mysql.TypeFloat) 46 ft.SetFlen(12) // Default 47 ft.SetDecimal(3) // Not Default 48 require.Equal(t, "float(12,3)", ft.String()) 49 ft = NewFieldType(mysql.TypeFloat) 50 ft.SetFlen(12) // Default 51 ft.SetDecimal(-1) // Default 52 require.Equal(t, "float", ft.String()) 53 ft = NewFieldType(mysql.TypeFloat) 54 ft.SetFlen(5) // Not Default 55 ft.SetDecimal(-1) // Default 56 require.Equal(t, "float", ft.String()) 57 ft = NewFieldType(mysql.TypeFloat) 58 ft.SetFlen(7) // Not Default 59 ft.SetDecimal(3) // Not Default 60 require.Equal(t, "float(7,3)", ft.String()) 61 require.False(t, HasCharset(ft)) 62 63 ft = NewFieldType(mysql.TypeDouble) 64 ft.SetFlen(22) // Default 65 ft.SetDecimal(3) // Not Default 66 require.Equal(t, "double(22,3)", ft.String()) 67 ft = NewFieldType(mysql.TypeDouble) 68 ft.SetFlen(22) // Default 69 ft.SetDecimal(-1) // Default 70 require.Equal(t, "double", ft.String()) 71 ft = NewFieldType(mysql.TypeDouble) 72 ft.SetFlen(5) // Not Default 73 ft.SetDecimal(-1) // Default 74 require.Equal(t, "double", ft.String()) 75 ft = NewFieldType(mysql.TypeDouble) 76 ft.SetFlen(7) // Not Default 77 ft.SetDecimal(3) // Not Default 78 require.Equal(t, "double(7,3)", ft.String()) 79 require.False(t, HasCharset(ft)) 80 81 ft = NewFieldType(mysql.TypeBlob) 82 ft.SetFlen(10) 83 ft.SetCharset("UTF8") 84 ft.SetCollate("UTF8_UNICODE_GI") 85 require.Equal(t, "text CHARACTER SET UTF8 COLLATE UTF8_UNICODE_GI", ft.String()) 86 require.True(t, HasCharset(ft)) 87 88 ft = NewFieldType(mysql.TypeVarchar) 89 ft.SetFlen(10) 90 ft.AddFlag(mysql.BinaryFlag) 91 require.Equal(t, "varchar(10) BINARY", ft.String()) 92 require.False(t, HasCharset(ft)) 93 94 ft = NewFieldType(mysql.TypeString) 95 ft.SetCharset(charset.CharsetBin) 96 ft.AddFlag(mysql.BinaryFlag) 97 require.Equal(t, "binary(1)", ft.String()) 98 require.False(t, HasCharset(ft)) 99 100 ft = NewFieldType(mysql.TypeEnum) 101 ft.SetElems([]string{"a", "b"}) 102 require.Equal(t, "enum('a','b')", ft.String()) 103 require.True(t, HasCharset(ft)) 104 105 ft = NewFieldType(mysql.TypeEnum) 106 ft.SetElems([]string{"'a'", "'b'"}) 107 require.Equal(t, "enum('''a''','''b''')", ft.String()) 108 require.True(t, HasCharset(ft)) 109 110 ft = NewFieldType(mysql.TypeEnum) 111 ft.SetElems([]string{"a\nb", "a\tb", "a\rb"}) 112 require.Equal(t, "enum('a\\nb','a\tb','a\\rb')", ft.String()) 113 require.True(t, HasCharset(ft)) 114 115 ft = NewFieldType(mysql.TypeEnum) 116 ft.SetElems([]string{"a\nb", "a'\t\r\nb", "a\rb"}) 117 require.Equal(t, "enum('a\\nb','a'' \\r\\nb','a\\rb')", ft.String()) 118 require.True(t, HasCharset(ft)) 119 120 ft = NewFieldType(mysql.TypeSet) 121 ft.SetElems([]string{"a", "b"}) 122 require.Equal(t, "set('a','b')", ft.String()) 123 require.True(t, HasCharset(ft)) 124 125 ft = NewFieldType(mysql.TypeSet) 126 ft.SetElems([]string{"'a'", "'b'"}) 127 require.Equal(t, "set('''a''','''b''')", ft.String()) 128 require.True(t, HasCharset(ft)) 129 130 ft = NewFieldType(mysql.TypeSet) 131 ft.SetElems([]string{"a\nb", "a'\t\r\nb", "a\rb"}) 132 require.Equal(t, "set('a\\nb','a'' \\r\\nb','a\\rb')", ft.String()) 133 require.True(t, HasCharset(ft)) 134 135 ft = NewFieldType(mysql.TypeSet) 136 ft.SetElems([]string{"a'\nb", "a'b\tc"}) 137 require.Equal(t, "set('a''\\nb','a''b c')", ft.String()) 138 require.True(t, HasCharset(ft)) 139 140 ft = NewFieldType(mysql.TypeTimestamp) 141 ft.SetFlen(8) 142 ft.SetDecimal(2) 143 require.Equal(t, "timestamp(2)", ft.String()) 144 require.False(t, HasCharset(ft)) 145 ft = NewFieldType(mysql.TypeTimestamp) 146 ft.SetFlen(8) 147 ft.SetDecimal(0) 148 require.Equal(t, "timestamp", ft.String()) 149 require.False(t, HasCharset(ft)) 150 151 ft = NewFieldType(mysql.TypeDatetime) 152 ft.SetFlen(8) 153 ft.SetDecimal(2) 154 require.Equal(t, "datetime(2)", ft.String()) 155 require.False(t, HasCharset(ft)) 156 ft = NewFieldType(mysql.TypeDatetime) 157 ft.SetFlen(8) 158 ft.SetDecimal(0) 159 require.Equal(t, "datetime", ft.String()) 160 require.False(t, HasCharset(ft)) 161 162 ft = NewFieldType(mysql.TypeDate) 163 ft.SetFlen(8) 164 ft.SetDecimal(2) 165 require.Equal(t, "date", ft.String()) 166 require.False(t, HasCharset(ft)) 167 ft = NewFieldType(mysql.TypeDate) 168 ft.SetFlen(8) 169 ft.SetDecimal(0) 170 require.Equal(t, "date", ft.String()) 171 require.False(t, HasCharset(ft)) 172 173 ft = NewFieldType(mysql.TypeYear) 174 ft.SetFlen(4) 175 ft.SetDecimal(0) 176 require.Equal(t, "year(4)", ft.String()) 177 require.False(t, HasCharset(ft)) 178 ft = NewFieldType(mysql.TypeYear) 179 ft.SetFlen(2) 180 ft.SetDecimal(2) 181 require.Equal(t, "year(2)", ft.String()) 182 require.False(t, HasCharset(ft)) 183 184 ft = NewFieldType(mysql.TypeVarchar) 185 ft.SetFlen(0) 186 ft.SetDecimal(0) 187 require.Equal(t, "varchar(0)", ft.String()) 188 require.True(t, HasCharset(ft)) 189 190 ft = NewFieldType(mysql.TypeString) 191 ft.SetFlen(0) 192 ft.SetDecimal(0) 193 require.Equal(t, "char(0)", ft.String()) 194 require.True(t, HasCharset(ft)) 195 } 196 197 func TestHasCharsetFromStmt(t *testing.T) { 198 template := "CREATE TABLE t(a %s)" 199 200 types := []struct { 201 strType string 202 hasCharset bool 203 }{ 204 {"int", false}, 205 {"real", false}, 206 {"float", false}, 207 {"bit", false}, 208 {"bool", false}, 209 {"char(1)", true}, 210 {"national char(1)", true}, 211 {"binary", false}, 212 {"varchar(1)", true}, 213 {"national varchar(1)", true}, 214 {"varbinary(1)", false}, 215 {"year", false}, 216 {"date", false}, 217 {"time", false}, 218 {"datetime", false}, 219 {"timestamp", false}, 220 {"blob", false}, 221 {"tinyblob", false}, 222 {"mediumblob", false}, 223 {"longblob", false}, 224 {"bit", false}, 225 {"text", true}, 226 {"tinytext", true}, 227 {"mediumtext", true}, 228 {"longtext", true}, 229 {"json", false}, 230 {"enum('1')", true}, 231 {"set('1')", true}, 232 } 233 234 p := parser.New() 235 for _, typ := range types { 236 sql := fmt.Sprintf(template, typ.strType) 237 stmt, err := p.ParseOneStmt(sql, "", "") 238 require.NoError(t, err) 239 240 col := stmt.(*ast.CreateTableStmt).Cols[0] 241 require.Equal(t, typ.hasCharset, HasCharset(col.Tp)) 242 } 243 } 244 245 func TestEnumSetFlen(t *testing.T) { 246 p := parser.New() 247 cases := []struct { 248 sql string 249 ex int 250 }{ 251 {"enum('a')", 1}, 252 {"enum('a', 'b')", 1}, 253 {"enum('a', 'bb')", 2}, 254 {"enum('a', 'b', 'c')", 1}, 255 {"enum('a', 'bb', 'c')", 2}, 256 {"enum('a', 'bb', 'c')", 2}, 257 {"enum('')", 0}, 258 {"enum('a', '')", 1}, 259 {"set('a')", 1}, 260 {"set('a', 'b')", 3}, 261 {"set('a', 'bb')", 4}, 262 {"set('a', 'b', 'c')", 5}, 263 {"set('a', 'bb', 'c')", 6}, 264 {"set('')", 0}, 265 {"set('a', '')", 2}, 266 } 267 268 for _, ca := range cases { 269 stmt, err := p.ParseOneStmt(fmt.Sprintf("create table t (e %v)", ca.sql), "", "") 270 require.NoError(t, err) 271 col := stmt.(*ast.CreateTableStmt).Cols[0] 272 require.Equal(t, ca.ex, col.Tp.GetFlen()) 273 } 274 } 275 276 func TestFieldTypeEqual(t *testing.T) { 277 // tp not equal 278 ft1 := NewFieldType(mysql.TypeDouble) 279 ft2 := NewFieldType(mysql.TypeFloat) 280 require.Equal(t, false, ft1.Equal(ft2)) 281 282 // decimal not equal 283 ft2 = NewFieldType(mysql.TypeDouble) 284 ft2.SetDecimal(5) 285 require.Equal(t, false, ft1.Equal(ft2)) 286 287 // flen not equal and decimal not -1 288 ft1.SetDecimal(5) 289 ft1.SetFlen(22) 290 require.Equal(t, false, ft1.Equal(ft2)) 291 292 // flen equal 293 ft2.SetFlen(22) 294 require.Equal(t, true, ft1.Equal(ft2)) 295 296 // decimal is -1 297 ft1.SetDecimal(-1) 298 ft2.SetDecimal(-1) 299 ft1.SetFlen(23) 300 require.Equal(t, true, ft1.Equal(ft2)) 301 } 302 303 func TestCompactStr(t *testing.T) { 304 cases := []struct { 305 t byte // Field Type 306 flen int // Field Length 307 flags uint // Field Flags, e.g. ZEROFILL 308 e1 string // Expected string with TiDBStrictIntegerDisplayWidth disabled 309 e2 string // Expected string with TiDBStrictIntegerDisplayWidth enabled 310 }{ 311 // TINYINT(1) is considered a bool by connectors, this should always display 312 // the display length. 313 {mysql.TypeTiny, 1, 0, `tinyint(1)`, `tinyint(1)`}, 314 {mysql.TypeTiny, 2, 0, `tinyint(2)`, `tinyint`}, 315 316 // If the ZEROFILL flag is set the display length should not be hidden. 317 {mysql.TypeLong, 10, 0, `int(10)`, `int`}, 318 {mysql.TypeLong, 10, mysql.ZerofillFlag, `int(10)`, `int(10)`}, 319 } 320 for _, cc := range cases { 321 ft := NewFieldType(cc.t) 322 ft.SetFlen(cc.flen) 323 ft.SetFlag(cc.flags) 324 325 TiDBStrictIntegerDisplayWidth = false 326 require.Equal(t, cc.e1, ft.CompactStr()) 327 328 TiDBStrictIntegerDisplayWidth = true 329 require.Equal(t, cc.e2, ft.CompactStr()) 330 } 331 }