Skip to content
This repository was archived by the owner on Jan 28, 2021. It is now read-only.

Commit 4c175cf

Browse files
Add CHAR type support
Signed-off-by: Alejandro García Montoro <[email protected]>
1 parent 70c0f52 commit 4c175cf

File tree

4 files changed

+89
-10
lines changed

4 files changed

+89
-10
lines changed

Diff for: engine_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -1994,7 +1994,7 @@ func TestDDL(t *testing.T) {
19941994
testQuery(t, e,
19951995
"CREATE TABLE t1(a INTEGER, b TEXT, c DATE, "+
19961996
"d TIMESTAMP, e VARCHAR(20), f BLOB NOT NULL, "+
1997-
"b1 BOOL, b2 BOOLEAN NOT NULL, g DATETIME",
1997+
"b1 BOOL, b2 BOOLEAN NOT NULL, g DATETIME, h CHAR(40))",
19981998
[]sql.Row(nil),
19991999
)
20002000

@@ -2014,6 +2014,7 @@ func TestDDL(t *testing.T) {
20142014
{Name: "b1", Type: sql.Uint8, Nullable: true, Source: "t1"},
20152015
{Name: "b2", Type: sql.Uint8, Source: "t1"},
20162016
{Name: "g", Type: sql.Datetime, Nullable: true, Source: "t1"},
2017+
{Name: "h", Type: sql.Text, Nullable: true, Source: "t1"},
20172018
}
20182019

20192020
require.Equal(s, testTable.Schema())

Diff for: sql/parse/parse_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
)
1414

1515
var fixtures = map[string]sql.Node{
16-
`CREATE TABLE t1(a INTEGER, b TEXT, c DATE, d TIMESTAMP, e VARCHAR(20), f BLOB NOT NULL, g DATETIME)`: plan.NewCreateTable(
16+
`CREATE TABLE t1(a INTEGER, b TEXT, c DATE, d TIMESTAMP, e VARCHAR(20), f BLOB NOT NULL, g DATETIME, h CHAR(40))`: plan.NewCreateTable(
1717
sql.UnresolvedDatabase(""),
1818
"t1",
1919
sql.Schema{{
@@ -44,6 +44,10 @@ var fixtures = map[string]sql.Node{
4444
Name: "g",
4545
Type: sql.Datetime,
4646
Nullable: true,
47+
}, {
48+
Name: "h",
49+
Type: sql.Text,
50+
Nullable: true,
4751
}},
4852
),
4953
`DESCRIBE TABLE foo;`: plan.NewDescribe(

Diff for: sql/type.go

+66-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ var (
2727
// ErrConvertingToTime is thrown when a value cannot be converted to a Time
2828
ErrConvertingToTime = errors.NewKind("value %q can't be converted to time.Time")
2929

30-
// ErrVarCharTruncation is thrown when a value is textually longer than the destination capacity
30+
// ErrCharTruncation is thrown when a Char value is textually longer than the destination capacity
31+
ErrCharTruncation = errors.NewKind("string value of %q is longer than destination capacity %d")
32+
33+
// ErrVarCharTruncation is thrown when a VarChar value is textually longer than the destination capacity
3134
ErrVarCharTruncation = errors.NewKind("string value of %q is longer than destination capacity %d")
3235

3336
// ErrValueNotNil is thrown when a value that was expected to be nil, is not
@@ -220,6 +223,11 @@ func Array(underlying Type) Type {
220223
return arrayT{underlying}
221224
}
222225

226+
// Char returns a new Char type of the given length.
227+
func Char(length int) Type {
228+
return charT{length: length}
229+
}
230+
223231
// VarChar returns a new VarChar type of the given length.
224232
func VarChar(length int) Type {
225233
return varCharT{length: length}
@@ -256,6 +264,10 @@ func MysqlTypeToType(sql query.Type) (Type, error) {
256264
return Date, nil
257265
case sqltypes.Text:
258266
return Text, nil
267+
case sqltypes.Char:
268+
// Since we can't get the size of the sqltypes.Char to instantiate a
269+
// specific Char(length) type we return a Text here
270+
return Text, nil
259271
case sqltypes.VarChar:
260272
// Since we can't get the size of the sqltypes.VarChar to instantiate a
261273
// specific VarChar(length) type we return a Text here
@@ -660,6 +672,50 @@ func (t datetimeT) Compare(a, b interface{}) (int, error) {
660672
return 0, nil
661673
}
662674

675+
type charT struct {
676+
length int
677+
}
678+
679+
func (t charT) Capacity() int { return t.length }
680+
681+
func (t charT) String() string { return fmt.Sprintf("CHAR(%d)", t.length) }
682+
683+
func (t charT) Type() query.Type {
684+
return sqltypes.Char
685+
}
686+
687+
func (t charT) SQL(v interface{}) (sqltypes.Value, error) {
688+
if v == nil {
689+
return sqltypes.MakeTrusted(sqltypes.Char, nil), nil
690+
}
691+
692+
v, err := t.Convert(v)
693+
if err != nil {
694+
return sqltypes.Value{}, err
695+
}
696+
697+
return sqltypes.MakeTrusted(sqltypes.Char, []byte(v.(string))), nil
698+
}
699+
700+
// Converts any value that can be casted to a string
701+
func (t charT) Convert(v interface{}) (interface{}, error) {
702+
val, err := cast.ToStringE(v)
703+
if err != nil {
704+
return nil, ErrConvertToSQL.New(t)
705+
}
706+
707+
if len(val) > t.length {
708+
return nil, ErrCharTruncation.New(val, t.length)
709+
}
710+
return val, nil
711+
}
712+
713+
// Compares two strings lexicographically
714+
func (t charT) Compare(a interface{}, b interface{}) (int, error) {
715+
return strings.Compare(a.(string), b.(string)), nil
716+
}
717+
718+
663719
type varCharT struct {
664720
length int
665721
}
@@ -1088,7 +1144,13 @@ func IsDecimal(t Type) bool {
10881144

10891145
// IsText checks if t is a text type.
10901146
func IsText(t Type) bool {
1091-
return t == Text || t == Blob || t == JSON || IsVarChar(t)
1147+
return t == Text || t == Blob || t == JSON || IsVarChar(t) || IsChar(t)
1148+
}
1149+
1150+
// IsChar checks if t is a Char type.
1151+
func IsChar(t Type) bool {
1152+
_, ok := t.(charT)
1153+
return ok
10921154
}
10931155

10941156
// IsVarChar checks if t is a varchar type.
@@ -1150,6 +1212,8 @@ func MySQLTypeName(t Type) string {
11501212
return "DATETIME"
11511213
case sqltypes.Date:
11521214
return "DATE"
1215+
case sqltypes.Char:
1216+
return "CHAR"
11531217
case sqltypes.VarChar:
11541218
return "VARCHAR"
11551219
case sqltypes.Text:

Diff for: sql/type_test.go

+16-6
Original file line numberDiff line numberDiff line change
@@ -342,20 +342,22 @@ func TestTuple(t *testing.T) {
342342
gt(t, typ, []interface{}{1, 2, 4}, []interface{}{1, 2, 3})
343343
}
344344

345-
func TestVarChar(t *testing.T) {
346-
typ := VarChar(3)
347-
require.True(t, IsVarChar(typ))
345+
// Generic test for Char and VarChar types.
346+
// genType should be sql.Char or sql.VarChar
347+
func testCharTypes(genType func(int) Type, checkType func(Type) bool, t *testing.T) {
348+
typ := genType(3)
349+
require.True(t, checkType(typ))
348350
require.True(t, IsText(typ))
349351
convert(t, typ, "foo", "foo")
350352
fooByte := []byte{'f', 'o', 'o'}
351353
convert(t, typ, fooByte, "foo")
352354

353-
typ = VarChar(1)
355+
typ = genType(1)
354356
convertErr(t, typ, "foo")
355357
convertErr(t, typ, fooByte)
356358
convertErr(t, typ, 123)
357359

358-
typ = VarChar(10)
360+
typ = genType(10)
359361
convert(t, typ, 123, "123")
360362
convertErr(t, typ, 1234567890123)
361363

@@ -370,10 +372,18 @@ func TestVarChar(t *testing.T) {
370372
require.NoError(t, err)
371373

372374
convert(t, typ, text, "abc")
373-
typ1 := VarChar(1)
375+
typ1 := genType(1)
374376
convertErr(t, typ1, text)
375377
}
376378

379+
func TestChar(t *testing.T) {
380+
testCharTypes(Char, IsChar, t)
381+
}
382+
383+
func TestVarChar(t *testing.T) {
384+
testCharTypes(VarChar, IsVarChar, t)
385+
}
386+
377387
func TestArray(t *testing.T) {
378388
require := require.New(t)
379389

0 commit comments

Comments
 (0)