Fix extends

This commit is contained in:
Lunny Xiao 2021-06-28 20:55:06 +08:00
parent 4a4f4300fe
commit 0fece7421c
No known key found for this signature in database
GPG Key ID: C3B7C91B632F738A
4 changed files with 332 additions and 6 deletions

3
.gitignore vendored
View File

@ -36,4 +36,5 @@ test.db.sql
*coverage.out
test.db
integrations/*.sql
integrations/test_sqlite*
integrations/test_sqlite*
cover.out

View File

@ -189,9 +189,6 @@ func (parser *Parser) parseFieldWithTags(table *schemas.Table, field reflect.Str
if h, ok := parser.handlers[ctx.tagUname]; ok {
if err := h(&ctx); err != nil {
if err == ErrIgnoreField {
continue
}
return nil, err
}
} else {

View File

@ -6,12 +6,14 @@ package tags
import (
"reflect"
"strings"
"testing"
"time"
"xorm.io/xorm/caches"
"xorm.io/xorm/dialects"
"xorm.io/xorm/names"
"xorm.io/xorm/schemas"
"github.com/stretchr/testify/assert"
)
@ -223,3 +225,328 @@ func TestParseWithTimes(t *testing.T) {
assert.True(t, table.Columns()[3].Nullable)
assert.True(t, table.Columns()[3].IsDeleted)
}
func TestParseWithExtends(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.SnakeMapper{},
names.GonicMapper{},
caches.NewManager(),
)
type StructWithEmbed struct {
Name string
CreatedAt time.Time `db:"created"`
UpdatedAt time.Time `db:"updated"`
DeletedAt time.Time `db:"deleted"`
}
type StructWithExtends struct {
SW StructWithEmbed `db:"extends"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithExtends)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_extends", table.Name)
assert.EqualValues(t, 4, len(table.Columns()))
assert.EqualValues(t, "name", table.Columns()[0].Name)
assert.EqualValues(t, "created_at", table.Columns()[1].Name)
assert.EqualValues(t, "updated_at", table.Columns()[2].Name)
assert.EqualValues(t, "deleted_at", table.Columns()[3].Name)
assert.True(t, table.Columns()[0].Nullable)
assert.True(t, table.Columns()[1].Nullable)
assert.True(t, table.Columns()[1].IsCreated)
assert.True(t, table.Columns()[2].Nullable)
assert.True(t, table.Columns()[2].IsUpdated)
assert.True(t, table.Columns()[3].Nullable)
assert.True(t, table.Columns()[3].IsDeleted)
}
func TestParseWithCache(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.SnakeMapper{},
names.GonicMapper{},
caches.NewManager(),
)
type StructWithCache struct {
Name string `db:"cache"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithCache)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_cache", table.Name)
assert.EqualValues(t, 1, len(table.Columns()))
assert.EqualValues(t, "name", table.Columns()[0].Name)
assert.True(t, table.Columns()[0].Nullable)
cacher := parser.cacherMgr.GetCacher(table.Name)
assert.NotNil(t, cacher)
}
func TestParseWithNoCache(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.SnakeMapper{},
names.GonicMapper{},
caches.NewManager(),
)
type StructWithNoCache struct {
Name string `db:"nocache"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithNoCache)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_no_cache", table.Name)
assert.EqualValues(t, 1, len(table.Columns()))
assert.EqualValues(t, "name", table.Columns()[0].Name)
assert.True(t, table.Columns()[0].Nullable)
cacher := parser.cacherMgr.GetCacher(table.Name)
assert.Nil(t, cacher)
}
func TestParseWithEnum(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.SnakeMapper{},
names.GonicMapper{},
caches.NewManager(),
)
type StructWithEnum struct {
Name string `db:"enum('alice', 'bob')"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithEnum)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_enum", table.Name)
assert.EqualValues(t, 1, len(table.Columns()))
assert.EqualValues(t, "name", table.Columns()[0].Name)
assert.True(t, table.Columns()[0].Nullable)
assert.EqualValues(t, schemas.Enum, strings.ToUpper(table.Columns()[0].SQLType.Name))
assert.EqualValues(t, map[string]int{
"alice": 0,
"bob": 1,
}, table.Columns()[0].EnumOptions)
}
func TestParseWithSet(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.SnakeMapper{},
names.GonicMapper{},
caches.NewManager(),
)
type StructWithSet struct {
Name string `db:"set('alice', 'bob')"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithSet)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_set", table.Name)
assert.EqualValues(t, 1, len(table.Columns()))
assert.EqualValues(t, "name", table.Columns()[0].Name)
assert.True(t, table.Columns()[0].Nullable)
assert.EqualValues(t, schemas.Set, strings.ToUpper(table.Columns()[0].SQLType.Name))
assert.EqualValues(t, map[string]int{
"alice": 0,
"bob": 1,
}, table.Columns()[0].SetOptions)
}
func TestParseWithIndex(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.SnakeMapper{},
names.GonicMapper{},
caches.NewManager(),
)
type StructWithIndex struct {
Name string `db:"index"`
Name2 string `db:"index(s)"`
Name3 string `db:"unique"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithIndex)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_index", table.Name)
assert.EqualValues(t, 3, len(table.Columns()))
assert.EqualValues(t, "name", table.Columns()[0].Name)
assert.EqualValues(t, "name2", table.Columns()[1].Name)
assert.EqualValues(t, "name3", table.Columns()[2].Name)
assert.True(t, table.Columns()[0].Nullable)
assert.True(t, table.Columns()[1].Nullable)
assert.True(t, table.Columns()[2].Nullable)
assert.EqualValues(t, 1, len(table.Columns()[0].Indexes))
assert.EqualValues(t, 1, len(table.Columns()[1].Indexes))
assert.EqualValues(t, 1, len(table.Columns()[2].Indexes))
}
func TestParseWithVersion(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.SnakeMapper{},
names.GonicMapper{},
caches.NewManager(),
)
type StructWithVersion struct {
Name string
Version int `db:"version"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithVersion)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_version", table.Name)
assert.EqualValues(t, 2, len(table.Columns()))
assert.EqualValues(t, "name", table.Columns()[0].Name)
assert.EqualValues(t, "version", table.Columns()[1].Name)
assert.True(t, table.Columns()[0].Nullable)
assert.True(t, table.Columns()[1].Nullable)
assert.True(t, table.Columns()[1].IsVersion)
}
func TestParseWithLocale(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.SnakeMapper{},
names.GonicMapper{},
caches.NewManager(),
)
type StructWithLocale struct {
UTCLocale time.Time `db:"utc"`
LocalLocale time.Time `db:"local"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithLocale)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_locale", table.Name)
assert.EqualValues(t, 2, len(table.Columns()))
assert.EqualValues(t, "utc_locale", table.Columns()[0].Name)
assert.EqualValues(t, "local_locale", table.Columns()[1].Name)
assert.EqualValues(t, time.UTC, table.Columns()[0].TimeZone)
assert.EqualValues(t, time.Local, table.Columns()[1].TimeZone)
}
func TestParseWithDefault(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.SnakeMapper{},
names.GonicMapper{},
caches.NewManager(),
)
type StructWithDefault struct {
Default1 time.Time `db:"default '1970-01-01 00:00:00'"`
Default2 time.Time `db:"default(CURRENT_TIMESTAMP)"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithDefault)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_default", table.Name)
assert.EqualValues(t, 2, len(table.Columns()))
assert.EqualValues(t, "default1", table.Columns()[0].Name)
assert.EqualValues(t, "default2", table.Columns()[1].Name)
assert.EqualValues(t, "'1970-01-01 00:00:00'", table.Columns()[0].Default)
assert.EqualValues(t, "CURRENT_TIMESTAMP", table.Columns()[1].Default)
}
func TestParseWithOnlyToDB(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.GonicMapper{
"DB": true,
},
names.SnakeMapper{},
caches.NewManager(),
)
type StructWithOnlyToDB struct {
Default1 time.Time `db:"->"`
Default2 time.Time `db:"<-"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithOnlyToDB)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_only_to_db", table.Name)
assert.EqualValues(t, 2, len(table.Columns()))
assert.EqualValues(t, "default1", table.Columns()[0].Name)
assert.EqualValues(t, "default2", table.Columns()[1].Name)
assert.EqualValues(t, schemas.ONLYTODB, table.Columns()[0].MapType)
assert.EqualValues(t, schemas.ONLYFROMDB, table.Columns()[1].MapType)
}
func TestParseWithJSON(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.GonicMapper{
"JSON": true,
},
names.SnakeMapper{},
caches.NewManager(),
)
type StructWithJSON struct {
Default1 []string `db:"json"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithJSON)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_json", table.Name)
assert.EqualValues(t, 1, len(table.Columns()))
assert.EqualValues(t, "default1", table.Columns()[0].Name)
assert.True(t, table.Columns()[0].IsJSON)
}
func TestParseWithSQLType(t *testing.T) {
parser := NewParser(
"db",
dialects.QueryDialect("mysql"),
names.GonicMapper{
"SQL": true,
},
names.GonicMapper{
"UUID": true,
},
caches.NewManager(),
)
type StructWithSQLType struct {
Col1 string `db:"varchar(32)"`
Col2 string `db:"char(32)"`
Int int64 `db:"bigint"`
DateTime time.Time `db:"datetime"`
UUID string `db:"uuid"`
}
table, err := parser.Parse(reflect.ValueOf(new(StructWithSQLType)))
assert.NoError(t, err)
assert.EqualValues(t, "struct_with_sql_type", table.Name)
assert.EqualValues(t, 5, len(table.Columns()))
assert.EqualValues(t, "col1", table.Columns()[0].Name)
assert.EqualValues(t, "col2", table.Columns()[1].Name)
assert.EqualValues(t, "int", table.Columns()[2].Name)
assert.EqualValues(t, "date_time", table.Columns()[3].Name)
assert.EqualValues(t, "uuid", table.Columns()[4].Name)
assert.EqualValues(t, "varchar", table.Columns()[0].SQLType.Name)
assert.EqualValues(t, "char", table.Columns()[1].SQLType.Name)
assert.EqualValues(t, "bigint", table.Columns()[2].SQLType.Name)
assert.EqualValues(t, "datetime", table.Columns()[3].SQLType.Name)
assert.EqualValues(t, "uuid", table.Columns()[4].SQLType.Name)
}

View File

@ -120,6 +120,7 @@ var (
"CACHE": CacheTagHandler,
"NOCACHE": NoCacheTagHandler,
"COMMENT": CommentTagHandler,
"EXTENDS": ExtendsTagHandler,
}
)
@ -270,7 +271,7 @@ func CommentTagHandler(ctx *Context) error {
// SQLTypeTagHandler describes SQL Type tag handler
func SQLTypeTagHandler(ctx *Context) error {
ctx.col.SQLType = schemas.SQLType{Name: ctx.tag.name}
ctx.col.SQLType = schemas.SQLType{Name: ctx.tagUname}
if ctx.tagUname == "JSON" {
ctx.col.IsJSON = true
}
@ -341,7 +342,7 @@ func ExtendsTagHandler(ctx *Context) error {
var tagPrefix = ctx.col.FieldName
if len(ctx.params) > 0 {
col.Nullable = isPtr
tagPrefix = ctx.params[0]
tagPrefix = strings.Trim(ctx.params[0], "'")
if col.IsPrimaryKey {
col.Name = ctx.col.FieldName
col.IsPrimaryKey = false