diff --git a/dialects/mssql.go b/dialects/mssql.go index 3b935396..0eeb1bcd 100644 --- a/dialects/mssql.go +++ b/dialects/mssql.go @@ -318,7 +318,7 @@ func (db *mssql) SQLType(c *schemas.Column) string { case schemas.TimeStampz: res = "DATETIMEOFFSET" c.Length = 7 - case schemas.MediumInt: + case schemas.MediumInt, schemas.TinyInt, schemas.SmallInt, schemas.UnsignedMediumInt, schemas.UnsignedTinyInt, schemas.UnsignedSmallInt: res = schemas.Int case schemas.Text, schemas.MediumText, schemas.TinyText, schemas.LongText, schemas.Json: res = db.defaultVarchar + "(MAX)" diff --git a/dialects/mysql.go b/dialects/mysql.go index 0ad68833..21128527 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -263,6 +263,7 @@ func (db *mysql) SetParams(params map[string]string) { func (db *mysql) SQLType(c *schemas.Column) string { var res string + var isUnsigned bool switch t := c.SQLType.Name; t { case schemas.Bool: res = schemas.TinyInt @@ -309,8 +310,19 @@ func (db *mysql) SQLType(c *schemas.Column) string { res = schemas.Text case schemas.UnsignedInt: res = schemas.Int + isUnsigned = true case schemas.UnsignedBigInt: res = schemas.BigInt + isUnsigned = true + case schemas.UnsignedMediumInt: + res = schemas.MediumInt + isUnsigned = true + case schemas.UnsignedSmallInt: + res = schemas.SmallInt + isUnsigned = true + case schemas.UnsignedTinyInt: + res = schemas.TinyInt + isUnsigned = true default: res = t } @@ -329,7 +341,7 @@ func (db *mysql) SQLType(c *schemas.Column) string { res += "(" + strconv.Itoa(c.Length) + ")" } - if c.SQLType.Name == schemas.UnsignedBigInt || c.SQLType.Name == schemas.UnsignedInt { + if isUnsigned { res += " UNSIGNED" } diff --git a/dialects/postgres.go b/dialects/postgres.go index cf760e18..96ebfc85 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -879,13 +879,13 @@ func (db *postgres) SetQuotePolicy(quotePolicy QuotePolicy) { func (db *postgres) SQLType(c *schemas.Column) string { var res string switch t := c.SQLType.Name; t { - case schemas.TinyInt: + case schemas.TinyInt, schemas.UnsignedTinyInt: res = schemas.SmallInt return res case schemas.Bit: res = schemas.Boolean return res - case schemas.MediumInt, schemas.Int, schemas.Integer: + case schemas.MediumInt, schemas.Int, schemas.Integer, schemas.UnsignedMediumInt, schemas.UnsignedSmallInt: if c.IsAutoIncrement { return schemas.Serial } diff --git a/dialects/sqlite3.go b/dialects/sqlite3.go index dae6bf93..ac17fd92 100644 --- a/dialects/sqlite3.go +++ b/dialects/sqlite3.go @@ -217,8 +217,9 @@ func (db *sqlite3) SQLType(c *schemas.Column) string { case schemas.Char, schemas.Varchar, schemas.NVarchar, schemas.TinyText, schemas.Text, schemas.MediumText, schemas.LongText, schemas.Json: return schemas.Text - case schemas.Bit, schemas.TinyInt, schemas.SmallInt, schemas.MediumInt, schemas.Int, schemas.Integer, schemas.BigInt, - schemas.UnsignedBigInt, schemas.UnsignedInt: + case schemas.Bit, schemas.TinyInt, schemas.UnsignedTinyInt, schemas.SmallInt, + schemas.UnsignedSmallInt, schemas.MediumInt, schemas.Int, schemas.UnsignedInt, + schemas.BigInt, schemas.UnsignedBigInt, schemas.Integer: return schemas.Integer case schemas.Float, schemas.Double, schemas.Real: return schemas.Real diff --git a/integrations/types_test.go b/integrations/types_test.go index 9d4e46a0..4bdbb6fe 100644 --- a/integrations/types_test.go +++ b/integrations/types_test.go @@ -493,6 +493,45 @@ func TestUnsignedUint32(t *testing.T) { assert.EqualValues(t, uint64(math.MaxUint32), v.Id) } +func TestUnsignedTinyInt(t *testing.T) { + type MyUnsignedTinyIntStruct struct { + Id uint8 `xorm:"unsigned tinyint"` + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(MyUnsignedTinyIntStruct)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + assert.EqualValues(t, 1, len(tables)) + assert.EqualValues(t, 1, len(tables[0].Columns())) + + switch testEngine.Dialect().URI().DBType { + case schemas.SQLITE: + assert.EqualValues(t, "INTEGER", tables[0].Columns()[0].SQLType.Name) + case schemas.MYSQL: + assert.EqualValues(t, "UNSIGNED TINYINT", tables[0].Columns()[0].SQLType.Name) + case schemas.POSTGRES: + assert.EqualValues(t, "SMALLINT", tables[0].Columns()[0].SQLType.Name) + case schemas.MSSQL: + assert.EqualValues(t, "INT", tables[0].Columns()[0].SQLType.Name) + default: + assert.False(t, true, "Unsigned is not implemented") + } + + cnt, err := testEngine.Insert(&MyUnsignedTinyIntStruct{ + Id: math.MaxUint8, + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var v MyUnsignedTinyIntStruct + has, err := testEngine.Get(&v) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, uint64(math.MaxUint32), v.Id) +} + type MyDecimal big.Int func (d *MyDecimal) FromDB(data []byte) error { diff --git a/schemas/type.go b/schemas/type.go index d64251bf..d799db08 100644 --- a/schemas/type.go +++ b/schemas/type.go @@ -92,16 +92,19 @@ func (s *SQLType) IsXML() bool { // enumerates all the database column types var ( - Bit = "BIT" - UnsignedBit = "UNSIGNED BIT" - TinyInt = "TINYINT" - SmallInt = "SMALLINT" - MediumInt = "MEDIUMINT" - Int = "INT" - UnsignedInt = "UNSIGNED INT" - Integer = "INTEGER" - BigInt = "BIGINT" - UnsignedBigInt = "UNSIGNED BIGINT" + Bit = "BIT" + UnsignedBit = "UNSIGNED BIT" + TinyInt = "TINYINT" + UnsignedTinyInt = "UNSIGNED TINYINT" + SmallInt = "SMALLINT" + UnsignedSmallInt = "UNSIGNED SMALLINT" + MediumInt = "MEDIUMINT" + UnsignedMediumInt = "UNSIGNED MEDIUMINT" + Int = "INT" + UnsignedInt = "UNSIGNED INT" + Integer = "INTEGER" + BigInt = "BIGINT" + UnsignedBigInt = "UNSIGNED BIGINT" Enum = "ENUM" Set = "SET" @@ -158,16 +161,19 @@ var ( Array = "ARRAY" SqlTypes = map[string]int{ - Bit: NUMERIC_TYPE, - UnsignedBit: NUMERIC_TYPE, - TinyInt: NUMERIC_TYPE, - SmallInt: NUMERIC_TYPE, - MediumInt: NUMERIC_TYPE, - Int: NUMERIC_TYPE, - UnsignedInt: NUMERIC_TYPE, - Integer: NUMERIC_TYPE, - BigInt: NUMERIC_TYPE, - UnsignedBigInt: NUMERIC_TYPE, + Bit: NUMERIC_TYPE, + UnsignedBit: NUMERIC_TYPE, + TinyInt: NUMERIC_TYPE, + UnsignedTinyInt: NUMERIC_TYPE, + SmallInt: NUMERIC_TYPE, + UnsignedSmallInt: NUMERIC_TYPE, + MediumInt: NUMERIC_TYPE, + UnsignedMediumInt: NUMERIC_TYPE, + Int: NUMERIC_TYPE, + UnsignedInt: NUMERIC_TYPE, + Integer: NUMERIC_TYPE, + BigInt: NUMERIC_TYPE, + UnsignedBigInt: NUMERIC_TYPE, Enum: TEXT_TYPE, Set: TEXT_TYPE, diff --git a/tags/parser.go b/tags/parser.go index 7d8c3bd6..e6245a68 100644 --- a/tags/parser.go +++ b/tags/parser.go @@ -217,6 +217,10 @@ func (parser *Parser) parseFieldWithTags(table *schemas.Table, fieldIndex int, f if col.SQLType.Name == "" { col.SQLType = schemas.Type2SQLType(field.Type) } + if ctx.isUnsigned && col.SQLType.IsNumeric() && !strings.HasPrefix(col.SQLType.Name, "UNSIGNED") { + col.SQLType.Name = "UNSIGNED " + col.SQLType.Name + } + parser.dialect.SQLType(col) if col.Length == 0 { col.Length = col.SQLType.DefaultLength diff --git a/tags/tag.go b/tags/tag.go index 32cdb37c..4e1f1ce7 100644 --- a/tags/tag.go +++ b/tags/tag.go @@ -93,6 +93,7 @@ type Context struct { hasCacheTag bool hasNoCacheTag bool ignoreNext bool + isUnsigned bool } // Handler describes tag handler for XORM @@ -122,6 +123,7 @@ var ( "NOCACHE": NoCacheTagHandler, "COMMENT": CommentTagHandler, "EXTENDS": ExtendsTagHandler, + "UNSIGNED": UnsignedTagHandler, } ) @@ -268,6 +270,12 @@ func UniqueTagHandler(ctx *Context) error { return nil } +// UnsignedTagHandler represents the column is unsigned +func UnsignedTagHandler(ctx *Context) error { + ctx.isUnsigned = true + return nil +} + // CommentTagHandler add comment to column func CommentTagHandler(ctx *Context) error { if len(ctx.params) > 0 {