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  }