From e2f910041961e080fb188c698ac2b58a6c7a7ecc Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 27 Feb 2020 03:58:31 +0000 Subject: [PATCH] Move tag parser related codes as a standalone sub package (#1547) Fix sliceEq fix tests Move tag parser related codes as a standalone sub package Reviewed-on: https://gitea.com/xorm/xorm/pulls/1547 --- .drone.yml | 4 +- convert.go | 7 - convert/conversion.go | 12 + engine.go | 231 +----- engine_cond.go | 3 +- engine_group.go | 8 +- engine_table.go | 14 +- helpers.go | 15 - internal/utils/slice.go | 22 + session.go | 7 +- session_convert.go | 9 +- session_delete.go | 2 +- session_find.go | 2 +- session_get.go | 2 +- session_insert.go | 2 +- session_schema.go | 5 +- statement.go | 5 +- tag_cache_test.go | 35 - tag_extends_test.go | 608 ---------------- tag_id_test.go | 85 --- tag_test.go | 600 ---------------- tag_version_test.go | 242 ------- tags/parser.go | 239 +++++++ tag.go => tags/tag.go | 58 +- tags/tag_test.go | 30 + tags_test.go | 1501 +++++++++++++++++++++++++++++++++++++++ types_test.go | 18 +- xorm.go | 6 +- 28 files changed, 1890 insertions(+), 1882 deletions(-) create mode 100644 convert/conversion.go create mode 100644 internal/utils/slice.go delete mode 100644 tag_cache_test.go delete mode 100644 tag_extends_test.go delete mode 100644 tag_id_test.go delete mode 100644 tag_test.go delete mode 100644 tag_version_test.go create mode 100644 tags/parser.go rename tag.go => tags/tag.go (83%) create mode 100644 tags/tag_test.go create mode 100644 tags_test.go diff --git a/.drone.yml b/.drone.yml index 3f859ee0..ef48cd60 100644 --- a/.drone.yml +++ b/.drone.yml @@ -22,8 +22,8 @@ steps: commands: - make test-sqlite - TEST_CACHE_ENABLE=true make test-sqlite - - go test ./caches/... ./core/... ./dialects/... ./internal/... \ - ./log/... ./migrate/... ./names/... ./schemas/... + - go test ./caches/... ./convert/... ./core/... ./dialects/... \ + ./log/... ./migrate/... ./names/... ./schemas/... ./tags/... when: event: - push diff --git a/convert.go b/convert.go index b8e1c4fc..2316ca0b 100644 --- a/convert.go +++ b/convert.go @@ -346,10 +346,3 @@ func asBool(bs []byte) (bool, error) { } return strconv.ParseBool(string(bs)) } - -// Conversion is an interface. A type implements Conversion will according -// the custom method to fill into database and retrieve from database. -type Conversion interface { - FromDB([]byte) error - ToDB() ([]byte, error) -} diff --git a/convert/conversion.go b/convert/conversion.go new file mode 100644 index 00000000..16f1a92a --- /dev/null +++ b/convert/conversion.go @@ -0,0 +1,12 @@ +// Copyright 2017 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package convert + +// Conversion is an interface. A type implements Conversion will according +// the custom method to fill into database and retrieve from database. +type Conversion interface { + FromDB([]byte) error + ToDB() ([]byte, error) +} diff --git a/engine.go b/engine.go index 242e3830..b97d1c06 100644 --- a/engine.go +++ b/engine.go @@ -28,6 +28,7 @@ import ( "xorm.io/xorm/log" "xorm.io/xorm/names" "xorm.io/xorm/schemas" + "xorm.io/xorm/tags" ) // Engine is the major struct of xorm, it means a database manager. @@ -36,10 +37,7 @@ type Engine struct { db *core.DB dialect dialects.Dialect - ColumnMapper names.Mapper - TableMapper names.Mapper - TagIdentifier string - Tables map[reflect.Type]*schemas.Table + Tables map[reflect.Type]*schemas.Table mutex *sync.RWMutex @@ -50,12 +48,12 @@ type Engine struct { TZLocation *time.Location // The timezone of the application DatabaseTZ *time.Location // The timezone of the database - tagHandlers map[string]tagHandler - engineGroup *EngineGroup - cacherMgr *caches.Manager defaultContext context.Context + + tagParser *tags.Parser + cacherMgr *caches.Manager } func (engine *Engine) SetCacher(tableName string, cacher caches.Cacher) { @@ -151,12 +149,12 @@ func (engine *Engine) SetMapper(mapper names.Mapper) { // SetTableMapper set the table name mapping rule func (engine *Engine) SetTableMapper(mapper names.Mapper) { - engine.TableMapper = mapper + engine.tagParser.TableMapper = mapper } // SetColumnMapper set the column name mapping rule func (engine *Engine) SetColumnMapper(mapper names.Mapper) { - engine.ColumnMapper = mapper + engine.tagParser.ColumnMapper = mapper } // SupportInsertMany If engine's database support batch insert records like @@ -769,7 +767,7 @@ func (engine *Engine) autoMapType(v reflect.Value) (*schemas.Table, error) { table, ok := engine.Tables[t] if !ok { var err error - table, err = engine.mapType(v) + table, err = engine.tagParser.MapType(v) if err != nil { return nil, err } @@ -813,215 +811,6 @@ func (engine *Engine) TableInfo(bean interface{}) *Table { return &Table{tb, engine.TableName(bean)} } -func addIndex(indexName string, table *schemas.Table, col *schemas.Column, indexType int) { - if index, ok := table.Indexes[indexName]; ok { - index.AddColumn(col.Name) - col.Indexes[index.Name] = indexType - } else { - index := schemas.NewIndex(indexName, indexType) - index.AddColumn(col.Name) - table.AddIndex(index) - col.Indexes[index.Name] = indexType - } -} - -// TableName table name interface to define customerize table name -type TableName interface { - TableName() string -} - -var ( - tpTableName = reflect.TypeOf((*TableName)(nil)).Elem() -) - -func (engine *Engine) mapType(v reflect.Value) (*schemas.Table, error) { - t := v.Type() - table := schemas.NewEmptyTable() - table.Type = t - table.Name = names.GetTableName(engine.TableMapper, v) - - var idFieldColName string - var hasCacheTag, hasNoCacheTag bool - - for i := 0; i < t.NumField(); i++ { - tag := t.Field(i).Tag - - ormTagStr := tag.Get(engine.TagIdentifier) - var col *schemas.Column - fieldValue := v.Field(i) - fieldType := fieldValue.Type() - - if ormTagStr != "" { - col = &schemas.Column{ - FieldName: t.Field(i).Name, - Nullable: true, - IsPrimaryKey: false, - IsAutoIncrement: false, - MapType: schemas.TWOSIDES, - Indexes: make(map[string]int), - DefaultIsEmpty: true, - } - tags := splitTag(ormTagStr) - - if len(tags) > 0 { - if tags[0] == "-" { - continue - } - - var ctx = tagContext{ - table: table, - col: col, - fieldValue: fieldValue, - indexNames: make(map[string]int), - engine: engine, - } - - if strings.HasPrefix(strings.ToUpper(tags[0]), "EXTENDS") { - pStart := strings.Index(tags[0], "(") - if pStart > -1 && strings.HasSuffix(tags[0], ")") { - var tagPrefix = strings.TrimFunc(tags[0][pStart+1:len(tags[0])-1], func(r rune) bool { - return r == '\'' || r == '"' - }) - - ctx.params = []string{tagPrefix} - } - - if err := ExtendsTagHandler(&ctx); err != nil { - return nil, err - } - continue - } - - for j, key := range tags { - if ctx.ignoreNext { - ctx.ignoreNext = false - continue - } - - k := strings.ToUpper(key) - ctx.tagName = k - ctx.params = []string{} - - pStart := strings.Index(k, "(") - if pStart == 0 { - return nil, errors.New("( could not be the first character") - } - if pStart > -1 { - if !strings.HasSuffix(k, ")") { - return nil, fmt.Errorf("field %s tag %s cannot match ) character", col.FieldName, key) - } - - ctx.tagName = k[:pStart] - ctx.params = strings.Split(key[pStart+1:len(k)-1], ",") - } - - if j > 0 { - ctx.preTag = strings.ToUpper(tags[j-1]) - } - if j < len(tags)-1 { - ctx.nextTag = tags[j+1] - } else { - ctx.nextTag = "" - } - - if h, ok := engine.tagHandlers[ctx.tagName]; ok { - if err := h(&ctx); err != nil { - return nil, err - } - } else { - if strings.HasPrefix(key, "'") && strings.HasSuffix(key, "'") { - col.Name = key[1 : len(key)-1] - } else { - col.Name = key - } - } - - if ctx.hasCacheTag { - hasCacheTag = true - } - if ctx.hasNoCacheTag { - hasNoCacheTag = true - } - } - - if col.SQLType.Name == "" { - col.SQLType = schemas.Type2SQLType(fieldType) - } - engine.dialect.SQLType(col) - if col.Length == 0 { - col.Length = col.SQLType.DefaultLength - } - if col.Length2 == 0 { - col.Length2 = col.SQLType.DefaultLength2 - } - if col.Name == "" { - col.Name = engine.ColumnMapper.Obj2Table(t.Field(i).Name) - } - - if ctx.isUnique { - ctx.indexNames[col.Name] = schemas.UniqueType - } else if ctx.isIndex { - ctx.indexNames[col.Name] = schemas.IndexType - } - - for indexName, indexType := range ctx.indexNames { - addIndex(indexName, table, col, indexType) - } - } - } else { - var sqlType schemas.SQLType - if fieldValue.CanAddr() { - if _, ok := fieldValue.Addr().Interface().(Conversion); ok { - sqlType = schemas.SQLType{Name: schemas.Text} - } - } - if _, ok := fieldValue.Interface().(Conversion); ok { - sqlType = schemas.SQLType{Name: schemas.Text} - } else { - sqlType = schemas.Type2SQLType(fieldType) - } - col = schemas.NewColumn(engine.ColumnMapper.Obj2Table(t.Field(i).Name), - t.Field(i).Name, sqlType, sqlType.DefaultLength, - sqlType.DefaultLength2, true) - - if fieldType.Kind() == reflect.Int64 && (strings.ToUpper(col.FieldName) == "ID" || strings.HasSuffix(strings.ToUpper(col.FieldName), ".ID")) { - idFieldColName = col.Name - } - } - if col.IsAutoIncrement { - col.Nullable = false - } - - table.AddColumn(col) - - } // end for - - if idFieldColName != "" && len(table.PrimaryKeys) == 0 { - col := table.GetColumn(idFieldColName) - col.IsPrimaryKey = true - col.IsAutoIncrement = true - col.Nullable = false - table.PrimaryKeys = append(table.PrimaryKeys, col.Name) - table.AutoIncrement = col.Name - } - - if hasCacheTag { - if engine.GetDefaultCacher() != nil { // !nash! use engine's cacher if provided - engine.logger.Info("enable cache on table:", table.Name) - engine.SetCacher(table.Name, engine.GetDefaultCacher()) - } else { - engine.logger.Info("enable LRU cache on table:", table.Name) - engine.SetCacher(table.Name, caches.NewLRUCacher2(caches.NewMemoryStore(), time.Hour, 10000)) - } - } - if hasNoCacheTag { - engine.logger.Info("disable cache on table:", table.Name) - engine.SetCacher(table.Name, nil) - } - - return table, nil -} - // IsTableEmpty if a table has any reocrd func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) { session := engine.NewSession() @@ -1542,12 +1331,12 @@ func (engine *Engine) formatTime(sqlTypeName string, t time.Time) (v interface{} // GetColumnMapper returns the column name mapper func (engine *Engine) GetColumnMapper() names.Mapper { - return engine.ColumnMapper + return engine.tagParser.ColumnMapper } // GetTableMapper returns the table name mapper func (engine *Engine) GetTableMapper() names.Mapper { - return engine.TableMapper + return engine.tagParser.TableMapper } // GetTZLocation returns time zone of the application diff --git a/engine_cond.go b/engine_cond.go index 949d705d..00bfd59d 100644 --- a/engine_cond.go +++ b/engine_cond.go @@ -12,6 +12,7 @@ import ( "time" "xorm.io/builder" + "xorm.io/xorm/convert" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -137,7 +138,7 @@ func (engine *Engine) buildConds(table *schemas.Table, bean interface{}, continue } val = engine.formatColTime(col, t) - } else if _, ok := reflect.New(fieldType).Interface().(Conversion); ok { + } else if _, ok := reflect.New(fieldType).Interface().(convert.Conversion); ok { continue } else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok { val, _ = valNul.Value() diff --git a/engine_group.go b/engine_group.go index 24dc0103..55159d55 100644 --- a/engine_group.go +++ b/engine_group.go @@ -112,9 +112,9 @@ func (eg *EngineGroup) Ping() error { // SetColumnMapper set the column name mapping rule func (eg *EngineGroup) SetColumnMapper(mapper names.Mapper) { - eg.Engine.ColumnMapper = mapper + eg.Engine.SetColumnMapper(mapper) for i := 0; i < len(eg.slaves); i++ { - eg.slaves[i].ColumnMapper = mapper + eg.slaves[i].SetColumnMapper(mapper) } } @@ -182,9 +182,9 @@ func (eg *EngineGroup) SetPolicy(policy GroupPolicy) *EngineGroup { // SetTableMapper set the table name mapping rule func (eg *EngineGroup) SetTableMapper(mapper names.Mapper) { - eg.Engine.TableMapper = mapper + eg.Engine.SetTableMapper(mapper) for i := 0; i < len(eg.slaves); i++ { - eg.slaves[i].TableMapper = mapper + eg.slaves[i].SetTableMapper(mapper) } } diff --git a/engine_table.go b/engine_table.go index e60d8fce..0954b2d3 100644 --- a/engine_table.go +++ b/engine_table.go @@ -72,13 +72,13 @@ func (engine *Engine) tbNameNoSchema(tablename interface{}) string { switch f.(type) { case string: table = f.(string) - case TableName: - table = f.(TableName).TableName() + case names.TableName: + table = f.(names.TableName).TableName() default: v := rValue(f) t := v.Type() if t.Kind() == reflect.Struct { - table = names.GetTableName(engine.TableMapper, v) + table = names.GetTableName(engine.GetTableMapper(), v) } else { table = engine.Quote(fmt.Sprintf("%v", f)) } @@ -90,18 +90,18 @@ func (engine *Engine) tbNameNoSchema(tablename interface{}) string { } else if l == 1 { return engine.Quote(table) } - case TableName: - return tablename.(TableName).TableName() + case names.TableName: + return tablename.(names.TableName).TableName() case string: return tablename.(string) case reflect.Value: v := tablename.(reflect.Value) - return names.GetTableName(engine.TableMapper, v) + return names.GetTableName(engine.GetTableMapper(), v) default: v := rValue(tablename) t := v.Type() if t.Kind() == reflect.Struct { - return names.GetTableName(engine.TableMapper, v) + return names.GetTableName(engine.GetTableMapper(), v) } return engine.Quote(fmt.Sprintf("%v", tablename)) } diff --git a/helpers.go b/helpers.go index e9a3a646..1401cbf2 100644 --- a/helpers.go +++ b/helpers.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" "reflect" - "sort" "strconv" "strings" "time" @@ -184,20 +183,6 @@ func structName(v reflect.Type) string { return v.Name() } -func sliceEq(left, right []string) bool { - if len(left) != len(right) { - return false - } - sort.Sort(sort.StringSlice(left)) - sort.Sort(sort.StringSlice(right)) - for i := 0; i < len(left); i++ { - if left[i] != right[i] { - return false - } - } - return true -} - func indexName(tableName, idxName string) string { return fmt.Sprintf("IDX_%v_%v", tableName, idxName) } diff --git a/internal/utils/slice.go b/internal/utils/slice.go new file mode 100644 index 00000000..89685706 --- /dev/null +++ b/internal/utils/slice.go @@ -0,0 +1,22 @@ +// Copyright 2020 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package utils + +import "sort" + +// SliceEq return true if two slice have the same elements even if different sort. +func SliceEq(left, right []string) bool { + if len(left) != len(right) { + return false + } + sort.Sort(sort.StringSlice(left)) + sort.Sort(sort.StringSlice(right)) + for i := 0; i < len(left); i++ { + if left[i] != right[i] { + return false + } + } + return true +} diff --git a/session.go b/session.go index 8c692879..d4d9f78a 100644 --- a/session.go +++ b/session.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "xorm.io/xorm/convert" "xorm.io/xorm/core" "xorm.io/xorm/schemas" ) @@ -453,7 +454,7 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b } if fieldValue.CanAddr() { - if structConvert, ok := fieldValue.Addr().Interface().(Conversion); ok { + if structConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok { if data, err := value2Bytes(&rawValue); err == nil { if err := structConvert.FromDB(data); err != nil { return nil, err @@ -465,12 +466,12 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b } } - if _, ok := fieldValue.Interface().(Conversion); ok { + if _, ok := fieldValue.Interface().(convert.Conversion); ok { if data, err := value2Bytes(&rawValue); err == nil { if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { fieldValue.Set(reflect.New(fieldValue.Type().Elem())) } - fieldValue.Interface().(Conversion).FromDB(data) + fieldValue.Interface().(convert.Conversion).FromDB(data) } else { return nil, err } diff --git a/session_convert.go b/session_convert.go index 1c19092d..04436ec6 100644 --- a/session_convert.go +++ b/session_convert.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "xorm.io/xorm/convert" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -91,11 +92,11 @@ var ( // convert a db data([]byte) to a field value func (session *Session) bytes2Value(col *schemas.Column, fieldValue *reflect.Value, data []byte) error { - if structConvert, ok := fieldValue.Addr().Interface().(Conversion); ok { + if structConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok { return structConvert.FromDB(data) } - if structConvert, ok := fieldValue.Interface().(Conversion); ok { + if structConvert, ok := fieldValue.Interface().(convert.Conversion); ok { return structConvert.FromDB(data) } @@ -539,7 +540,7 @@ func (session *Session) bytes2Value(col *schemas.Column, fieldValue *reflect.Val // convert a field value of a struct to interface for put into db func (session *Session) value2Interface(col *schemas.Column, fieldValue reflect.Value) (interface{}, error) { if fieldValue.CanAddr() { - if fieldConvert, ok := fieldValue.Addr().Interface().(Conversion); ok { + if fieldConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok { data, err := fieldConvert.ToDB() if err != nil { return 0, err @@ -551,7 +552,7 @@ func (session *Session) value2Interface(col *schemas.Column, fieldValue reflect. } } - if fieldConvert, ok := fieldValue.Interface().(Conversion); ok { + if fieldConvert, ok := fieldValue.Interface().(convert.Conversion); ok { data, err := fieldConvert.ToDB() if err != nil { return 0, err diff --git a/session_delete.go b/session_delete.go index a639d61f..6bcb3852 100644 --- a/session_delete.go +++ b/session_delete.go @@ -28,7 +28,7 @@ func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr stri return ErrCacheFailed } - cacher := session.engine.GetCacher(tableName) + cacher := session.engine.cacherMgr.GetCacher(tableName) pkColumns := table.PKColumns() ids, err := caches.GetCacheSql(cacher, tableName, newsql, args) if err != nil { diff --git a/session_find.go b/session_find.go index fd1d49b1..492f19e6 100644 --- a/session_find.go +++ b/session_find.go @@ -329,7 +329,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in } tableName := session.statement.TableName() - cacher := session.engine.GetCacher(tableName) + cacher := session.engine.cacherMgr.GetCacher(tableName) if cacher == nil { return nil } diff --git a/session_get.go b/session_get.go index bf91eacf..d1e96958 100644 --- a/session_get.go +++ b/session_get.go @@ -280,7 +280,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf } tableName := session.statement.TableName() - cacher := session.engine.GetCacher(tableName) + cacher := session.engine.cacherMgr.GetCacher(tableName) session.engine.logger.Debug("[cache] Get SQL:", newsql, args) table := session.statement.RefTable diff --git a/session_insert.go b/session_insert.go index fcd7b2b5..4e822247 100644 --- a/session_insert.go +++ b/session_insert.go @@ -613,7 +613,7 @@ func (session *Session) cacheInsert(table string) error { if !session.statement.UseCache { return nil } - cacher := session.engine.GetCacher(table) + cacher := session.engine.cacherMgr.GetCacher(table) if cacher == nil { return nil } diff --git a/session_schema.go b/session_schema.go index 31b2ea42..809f158f 100644 --- a/session_schema.go +++ b/session_schema.go @@ -9,6 +9,7 @@ import ( "fmt" "strings" + "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -188,7 +189,7 @@ func (session *Session) isIndexExist2(tableName string, cols []string, unique bo } for _, index := range indexes { - if sliceEq(index.Cols, cols) { + if utils.SliceEq(index.Cols, cols) { if unique { return index.Type == schemas.UniqueType, nil } @@ -241,7 +242,7 @@ func (session *Session) Sync2(beans ...interface{}) error { for _, bean := range beans { v := rValue(bean) - table, err := engine.mapType(v) + table, err := engine.tagParser.MapType(v) if err != nil { return err } diff --git a/statement.go b/statement.go index 651ce175..d3048601 100644 --- a/statement.go +++ b/statement.go @@ -12,6 +12,7 @@ import ( "time" "xorm.io/builder" + "xorm.io/xorm/convert" "xorm.io/xorm/dialects" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" @@ -329,7 +330,7 @@ func (statement *Statement) buildUpdates(bean interface{}, var val interface{} if fieldValue.CanAddr() { - if structConvert, ok := fieldValue.Addr().Interface().(Conversion); ok { + if structConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok { data, err := structConvert.ToDB() if err != nil { engine.logger.Error(err) @@ -340,7 +341,7 @@ func (statement *Statement) buildUpdates(bean interface{}, } } - if structConvert, ok := fieldValue.Interface().(Conversion); ok { + if structConvert, ok := fieldValue.Interface().(convert.Conversion); ok { data, err := structConvert.ToDB() if err != nil { engine.logger.Error(err) diff --git a/tag_cache_test.go b/tag_cache_test.go deleted file mode 100644 index 30e2c51a..00000000 --- a/tag_cache_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2017 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestCacheTag(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type CacheDomain struct { - Id int64 `xorm:"pk cache"` - Name string - } - - assert.NoError(t, testEngine.CreateTables(&CacheDomain{})) - assert.True(t, testEngine.GetCacher(testEngine.TableName(&CacheDomain{})) != nil) -} - -func TestNoCacheTag(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type NoCacheDomain struct { - Id int64 `xorm:"pk nocache"` - Name string - } - - assert.NoError(t, testEngine.CreateTables(&NoCacheDomain{})) - assert.True(t, testEngine.GetCacher(testEngine.TableName(&NoCacheDomain{})) == nil) -} diff --git a/tag_extends_test.go b/tag_extends_test.go deleted file mode 100644 index 80540b57..00000000 --- a/tag_extends_test.go +++ /dev/null @@ -1,608 +0,0 @@ -// Copyright 2017 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "errors" - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "xorm.io/xorm/schemas" -) - -type tempUser struct { - Id int64 - Username string -} - -type tempUser2 struct { - TempUser tempUser `xorm:"extends"` - Departname string -} - -type tempUser3 struct { - Temp *tempUser `xorm:"extends"` - Departname string -} - -type tempUser4 struct { - TempUser2 tempUser2 `xorm:"extends"` -} - -type Userinfo struct { - Uid int64 `xorm:"id pk not null autoincr"` - Username string `xorm:"unique"` - Departname string - Alias string `xorm:"-"` - Created time.Time - Detail Userdetail `xorm:"detail_id int(11)"` - Height float64 - Avatar []byte - IsMan bool -} - -type Userdetail struct { - Id int64 - Intro string `xorm:"text"` - Profile string `xorm:"varchar(2000)"` -} - -type UserAndDetail struct { - Userinfo `xorm:"extends"` - Userdetail `xorm:"extends"` -} - -func TestExtends(t *testing.T) { - assert.NoError(t, prepareEngine()) - - err := testEngine.DropTables(&tempUser2{}) - assert.NoError(t, err) - - err = testEngine.CreateTables(&tempUser2{}) - assert.NoError(t, err) - - tu := &tempUser2{tempUser{0, "extends"}, "dev depart"} - _, err = testEngine.Insert(tu) - assert.NoError(t, err) - - tu2 := &tempUser2{} - _, err = testEngine.Get(tu2) - assert.NoError(t, err) - - tu3 := &tempUser2{tempUser{0, "extends update"}, ""} - _, err = testEngine.ID(tu2.TempUser.Id).Update(tu3) - assert.NoError(t, err) - - err = testEngine.DropTables(&tempUser4{}) - assert.NoError(t, err) - - err = testEngine.CreateTables(&tempUser4{}) - assert.NoError(t, err) - - tu8 := &tempUser4{tempUser2{tempUser{0, "extends"}, "dev depart"}} - _, err = testEngine.Insert(tu8) - assert.NoError(t, err) - - tu9 := &tempUser4{} - _, err = testEngine.Get(tu9) - assert.NoError(t, err) - - if tu9.TempUser2.TempUser.Username != tu8.TempUser2.TempUser.Username || tu9.TempUser2.Departname != tu8.TempUser2.Departname { - err = errors.New(fmt.Sprintln("not equal for", tu8, tu9)) - t.Error(err) - panic(err) - } - - tu10 := &tempUser4{tempUser2{tempUser{0, "extends update"}, ""}} - _, err = testEngine.ID(tu9.TempUser2.TempUser.Id).Update(tu10) - assert.NoError(t, err) - - err = testEngine.DropTables(&tempUser3{}) - assert.NoError(t, err) - - err = testEngine.CreateTables(&tempUser3{}) - assert.NoError(t, err) - - tu4 := &tempUser3{&tempUser{0, "extends"}, "dev depart"} - _, err = testEngine.Insert(tu4) - assert.NoError(t, err) - - tu5 := &tempUser3{} - _, err = testEngine.Get(tu5) - assert.NoError(t, err) - - if tu5.Temp == nil { - err = errors.New("error get data extends") - t.Error(err) - panic(err) - } - if tu5.Temp.Id != 1 || tu5.Temp.Username != "extends" || - tu5.Departname != "dev depart" { - err = errors.New("error get data extends") - t.Error(err) - panic(err) - } - - tu6 := &tempUser3{&tempUser{0, "extends update"}, ""} - _, err = testEngine.ID(tu5.Temp.Id).Update(tu6) - assert.NoError(t, err) - - users := make([]tempUser3, 0) - err = testEngine.Find(&users) - assert.NoError(t, err) - assert.EqualValues(t, 1, len(users), "error get data not 1") - - assertSync(t, new(Userinfo), new(Userdetail)) - - detail := Userdetail{ - Intro: "I'm in China", - } - _, err = testEngine.Insert(&detail) - assert.NoError(t, err) - - _, err = testEngine.Insert(&Userinfo{ - Username: "lunny", - Detail: detail, - }) - assert.NoError(t, err) - - var info UserAndDetail - qt := testEngine.Quote - ui := testEngine.TableName(new(Userinfo), true) - ud := testEngine.TableName(&detail, true) - uiid := testEngine.GetColumnMapper().Obj2Table("Id") - udid := "detail_id" - sql := fmt.Sprintf("select * from %s, %s where %s.%s = %s.%s", - qt(ui), qt(ud), qt(ui), qt(udid), qt(ud), qt(uiid)) - b, err := testEngine.SQL(sql).NoCascade().Get(&info) - assert.NoError(t, err) - if !b { - err = errors.New("should has lest one record") - t.Error(err) - panic(err) - } - fmt.Println(info) - if info.Userinfo.Uid == 0 || info.Userdetail.Id == 0 { - err = errors.New("all of the id should has value") - t.Error(err) - panic(err) - } - - fmt.Println("----join--info2") - var info2 UserAndDetail - b, err = testEngine.Table(&Userinfo{}). - Join("LEFT", qt(ud), qt(ui)+"."+qt("detail_id")+" = "+qt(ud)+"."+qt(uiid)). - NoCascade().Get(&info2) - if err != nil { - t.Error(err) - panic(err) - } - if !b { - err = errors.New("should has lest one record") - t.Error(err) - panic(err) - } - if info2.Userinfo.Uid == 0 || info2.Userdetail.Id == 0 { - err = errors.New("all of the id should has value") - t.Error(err) - panic(err) - } - fmt.Println(info2) - - fmt.Println("----join--infos2") - var infos2 = make([]UserAndDetail, 0) - err = testEngine.Table(&Userinfo{}). - Join("LEFT", qt(ud), qt(ui)+"."+qt("detail_id")+" = "+qt(ud)+"."+qt(uiid)). - NoCascade(). - Find(&infos2) - assert.NoError(t, err) - fmt.Println(infos2) -} - -type MessageBase struct { - Id int64 `xorm:"int(11) pk autoincr"` - TypeId int64 `xorm:"int(11) notnull"` -} - -type Message struct { - MessageBase `xorm:"extends"` - Title string `xorm:"varchar(100) notnull"` - Content string `xorm:"text notnull"` - Uid int64 `xorm:"int(11) notnull"` - ToUid int64 `xorm:"int(11) notnull"` - CreateTime time.Time `xorm:"datetime notnull created"` -} - -type MessageUser struct { - Id int64 - Name string -} - -type MessageType struct { - Id int64 - Name string -} - -type MessageExtend3 struct { - Message `xorm:"extends"` - Sender MessageUser `xorm:"extends"` - Receiver MessageUser `xorm:"extends"` - Type MessageType `xorm:"extends"` -} - -type MessageExtend4 struct { - Message `xorm:"extends"` - MessageUser `xorm:"extends"` - MessageType `xorm:"extends"` -} - -func TestExtends2(t *testing.T) { - assert.NoError(t, prepareEngine()) - - err := testEngine.DropTables(&Message{}, &MessageUser{}, &MessageType{}) - assert.NoError(t, err) - - err = testEngine.CreateTables(&Message{}, &MessageUser{}, &MessageType{}) - assert.NoError(t, err) - - var sender = MessageUser{Name: "sender"} - var receiver = MessageUser{Name: "receiver"} - var msgtype = MessageType{Name: "type"} - _, err = testEngine.Insert(&sender, &receiver, &msgtype) - assert.NoError(t, err) - - msg := Message{ - MessageBase: MessageBase{ - Id: msgtype.Id, - }, - Title: "test", - Content: "test", - Uid: sender.Id, - ToUid: receiver.Id, - } - - session := testEngine.NewSession() - defer session.Close() - - // MSSQL deny insert identity column excep declare as below - if testEngine.Dialect().DBType() == schemas.MSSQL { - err = session.Begin() - assert.NoError(t, err) - _, err = session.Exec("SET IDENTITY_INSERT message ON") - assert.NoError(t, err) - } - cnt, err := session.Insert(&msg) - assert.NoError(t, err) - assert.EqualValues(t, 1, cnt) - - if testEngine.Dialect().DBType() == schemas.MSSQL { - err = session.Commit() - assert.NoError(t, err) - } - - var mapper = testEngine.GetTableMapper().Obj2Table - var quote = testEngine.Quote - userTableName := quote(testEngine.TableName(mapper("MessageUser"), true)) - typeTableName := quote(testEngine.TableName(mapper("MessageType"), true)) - msgTableName := quote(testEngine.TableName(mapper("Message"), true)) - - list := make([]Message, 0) - err = session.Table(msgTableName).Join("LEFT", []string{userTableName, "sender"}, "`sender`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Uid")+"`"). - Join("LEFT", []string{userTableName, "receiver"}, "`receiver`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("ToUid")+"`"). - Join("LEFT", []string{typeTableName, "type"}, "`type`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Id")+"`"). - Find(&list) - assert.NoError(t, err) - - assert.EqualValues(t, 1, len(list), fmt.Sprintln("should have 1 message, got", len(list))) - assert.EqualValues(t, msg.Id, list[0].Id, fmt.Sprintln("should message equal", list[0], msg)) -} - -func TestExtends3(t *testing.T) { - assert.NoError(t, prepareEngine()) - - err := testEngine.DropTables(&Message{}, &MessageUser{}, &MessageType{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = testEngine.CreateTables(&Message{}, &MessageUser{}, &MessageType{}) - if err != nil { - t.Error(err) - panic(err) - } - - var sender = MessageUser{Name: "sender"} - var receiver = MessageUser{Name: "receiver"} - var msgtype = MessageType{Name: "type"} - _, err = testEngine.Insert(&sender, &receiver, &msgtype) - if err != nil { - t.Error(err) - panic(err) - } - - msg := Message{ - MessageBase: MessageBase{ - Id: msgtype.Id, - }, - Title: "test", - Content: "test", - Uid: sender.Id, - ToUid: receiver.Id, - } - - session := testEngine.NewSession() - defer session.Close() - - // MSSQL deny insert identity column excep declare as below - if testEngine.Dialect().DBType() == schemas.MSSQL { - err = session.Begin() - assert.NoError(t, err) - _, err = session.Exec("SET IDENTITY_INSERT message ON") - assert.NoError(t, err) - } - _, err = session.Insert(&msg) - assert.NoError(t, err) - - if testEngine.Dialect().DBType() == schemas.MSSQL { - err = session.Commit() - assert.NoError(t, err) - } - - var mapper = testEngine.GetTableMapper().Obj2Table - var quote = testEngine.Quote - userTableName := quote(testEngine.TableName(mapper("MessageUser"), true)) - typeTableName := quote(testEngine.TableName(mapper("MessageType"), true)) - msgTableName := quote(testEngine.TableName(mapper("Message"), true)) - - list := make([]MessageExtend3, 0) - err = session.Table(msgTableName).Join("LEFT", []string{userTableName, "sender"}, "`sender`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Uid")+"`"). - Join("LEFT", []string{userTableName, "receiver"}, "`receiver`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("ToUid")+"`"). - Join("LEFT", []string{typeTableName, "type"}, "`type`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Id")+"`"). - Find(&list) - assert.NoError(t, err) - - if len(list) != 1 { - err = errors.New(fmt.Sprintln("should have 1 message, got", len(list))) - t.Error(err) - panic(err) - } - - if list[0].Message.Id != msg.Id { - err = errors.New(fmt.Sprintln("should message equal", list[0].Message, msg)) - t.Error(err) - panic(err) - } - - if list[0].Sender.Id != sender.Id || list[0].Sender.Name != sender.Name { - err = errors.New(fmt.Sprintln("should sender equal", list[0].Sender, sender)) - t.Error(err) - panic(err) - } - - if list[0].Receiver.Id != receiver.Id || list[0].Receiver.Name != receiver.Name { - err = errors.New(fmt.Sprintln("should receiver equal", list[0].Receiver, receiver)) - t.Error(err) - panic(err) - } - - if list[0].Type.Id != msgtype.Id || list[0].Type.Name != msgtype.Name { - err = errors.New(fmt.Sprintln("should msgtype equal", list[0].Type, msgtype)) - t.Error(err) - panic(err) - } -} - -func TestExtends4(t *testing.T) { - assert.NoError(t, prepareEngine()) - - err := testEngine.DropTables(&Message{}, &MessageUser{}, &MessageType{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = testEngine.CreateTables(&Message{}, &MessageUser{}, &MessageType{}) - if err != nil { - t.Error(err) - panic(err) - } - - var sender = MessageUser{Name: "sender"} - var msgtype = MessageType{Name: "type"} - _, err = testEngine.Insert(&sender, &msgtype) - if err != nil { - t.Error(err) - panic(err) - } - - msg := Message{ - MessageBase: MessageBase{ - Id: msgtype.Id, - }, - Title: "test", - Content: "test", - Uid: sender.Id, - } - - session := testEngine.NewSession() - defer session.Close() - - // MSSQL deny insert identity column excep declare as below - if testEngine.Dialect().DBType() == schemas.MSSQL { - err = session.Begin() - assert.NoError(t, err) - _, err = session.Exec("SET IDENTITY_INSERT message ON") - assert.NoError(t, err) - } - _, err = session.Insert(&msg) - assert.NoError(t, err) - - if testEngine.Dialect().DBType() == schemas.MSSQL { - err = session.Commit() - assert.NoError(t, err) - } - - var mapper = testEngine.GetTableMapper().Obj2Table - var quote = testEngine.Quote - userTableName := quote(testEngine.TableName(mapper("MessageUser"), true)) - typeTableName := quote(testEngine.TableName(mapper("MessageType"), true)) - msgTableName := quote(testEngine.TableName(mapper("Message"), true)) - - list := make([]MessageExtend4, 0) - err = session.Table(msgTableName).Join("LEFT", userTableName, userTableName+".`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Uid")+"`"). - Join("LEFT", typeTableName, typeTableName+".`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Id")+"`"). - Find(&list) - if err != nil { - t.Error(err) - panic(err) - } - - if len(list) != 1 { - err = errors.New(fmt.Sprintln("should have 1 message, got", len(list))) - t.Error(err) - panic(err) - } - - if list[0].Message.Id != msg.Id { - err = errors.New(fmt.Sprintln("should message equal", list[0].Message, msg)) - t.Error(err) - panic(err) - } - - if list[0].MessageUser.Id != sender.Id || list[0].MessageUser.Name != sender.Name { - err = errors.New(fmt.Sprintln("should sender equal", list[0].MessageUser, sender)) - t.Error(err) - panic(err) - } - - if list[0].MessageType.Id != msgtype.Id || list[0].MessageType.Name != msgtype.Name { - err = errors.New(fmt.Sprintln("should msgtype equal", list[0].MessageType, msgtype)) - t.Error(err) - panic(err) - } -} - -type Size struct { - ID int64 `xorm:"int(4) 'id' pk autoincr"` - Width float32 `json:"width" xorm:"float 'Width'"` - Height float32 `json:"height" xorm:"float 'Height'"` -} - -type Book struct { - ID int64 `xorm:"int(4) 'id' pk autoincr"` - SizeOpen *Size `xorm:"extends('Open')"` - SizeClosed *Size `xorm:"extends('Closed')"` - Size *Size `xorm:"extends('')"` -} - -func TestExtends5(t *testing.T) { - assert.NoError(t, prepareEngine()) - err := testEngine.DropTables(&Book{}, &Size{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = testEngine.CreateTables(&Size{}, &Book{}) - if err != nil { - t.Error(err) - panic(err) - } - - var sc = Size{Width: 0.2, Height: 0.4} - var so = Size{Width: 0.2, Height: 0.8} - var s = Size{Width: 0.15, Height: 1.5} - var bk1 = Book{ - SizeOpen: &so, - SizeClosed: &sc, - Size: &s, - } - var bk2 = Book{ - SizeOpen: &so, - } - var bk3 = Book{ - SizeClosed: &sc, - Size: &s, - } - var bk4 = Book{} - var bk5 = Book{Size: &s} - _, err = testEngine.Insert(&sc, &so, &s, &bk1, &bk2, &bk3, &bk4, &bk5) - if err != nil { - t.Fatal(err) - } - - var books = map[int64]Book{ - bk1.ID: bk1, - bk2.ID: bk2, - bk3.ID: bk3, - bk4.ID: bk4, - bk5.ID: bk5, - } - - session := testEngine.NewSession() - defer session.Close() - - var mapper = testEngine.GetTableMapper().Obj2Table - var quote = testEngine.Quote - bookTableName := quote(testEngine.TableName(mapper("Book"), true)) - sizeTableName := quote(testEngine.TableName(mapper("Size"), true)) - - list := make([]Book, 0) - err = session. - Select(fmt.Sprintf( - "%s.%s, sc.%s AS %s, sc.%s AS %s, s.%s, s.%s", - quote(bookTableName), - quote("id"), - quote("Width"), - quote("ClosedWidth"), - quote("Height"), - quote("ClosedHeight"), - quote("Width"), - quote("Height"), - )). - Table(bookTableName). - Join( - "LEFT", - sizeTableName+" AS `sc`", - bookTableName+".`SizeClosed`=sc.`id`", - ). - Join( - "LEFT", - sizeTableName+" AS `s`", - bookTableName+".`Size`=s.`id`", - ). - Find(&list) - if err != nil { - t.Error(err) - panic(err) - } - - for _, book := range list { - if ok := assert.Equal(t, books[book.ID].SizeClosed.Width, book.SizeClosed.Width); !ok { - t.Error("Not bounded size closed") - panic("Not bounded size closed") - } - - if ok := assert.Equal(t, books[book.ID].SizeClosed.Height, book.SizeClosed.Height); !ok { - t.Error("Not bounded size closed") - panic("Not bounded size closed") - } - - if books[book.ID].Size != nil || book.Size != nil { - if ok := assert.Equal(t, books[book.ID].Size.Width, book.Size.Width); !ok { - t.Error("Not bounded size") - panic("Not bounded size") - } - - if ok := assert.Equal(t, books[book.ID].Size.Height, book.Size.Height); !ok { - t.Error("Not bounded size") - panic("Not bounded size") - } - } - } -} diff --git a/tag_id_test.go b/tag_id_test.go deleted file mode 100644 index a386b783..00000000 --- a/tag_id_test.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2017 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "xorm.io/xorm/names" -) - -type IDGonicMapper struct { - ID int64 -} - -func TestGonicMapperID(t *testing.T) { - assert.NoError(t, prepareEngine()) - - oldMapper := testEngine.GetColumnMapper() - testEngine.UnMapType(rValue(new(IDGonicMapper)).Type()) - testEngine.SetMapper(names.LintGonicMapper) - defer func() { - testEngine.UnMapType(rValue(new(IDGonicMapper)).Type()) - testEngine.SetMapper(oldMapper) - }() - - err := testEngine.CreateTables(new(IDGonicMapper)) - if err != nil { - t.Fatal(err) - } - - tables, err := testEngine.DBMetas() - if err != nil { - t.Fatal(err) - } - - for _, tb := range tables { - if tb.Name == "id_gonic_mapper" { - if len(tb.PKColumns()) != 1 || tb.PKColumns()[0].Name != "id" { - t.Fatal(tb) - } - return - } - } - - t.Fatal("not table id_gonic_mapper") -} - -type IDSameMapper struct { - ID int64 -} - -func TestSameMapperID(t *testing.T) { - assert.NoError(t, prepareEngine()) - - oldMapper := testEngine.GetColumnMapper() - testEngine.UnMapType(rValue(new(IDSameMapper)).Type()) - testEngine.SetMapper(names.SameMapper{}) - defer func() { - testEngine.UnMapType(rValue(new(IDSameMapper)).Type()) - testEngine.SetMapper(oldMapper) - }() - - err := testEngine.CreateTables(new(IDSameMapper)) - if err != nil { - t.Fatal(err) - } - - tables, err := testEngine.DBMetas() - if err != nil { - t.Fatal(err) - } - - for _, tb := range tables { - if tb.Name == "IDSameMapper" { - if len(tb.PKColumns()) != 1 || tb.PKColumns()[0].Name != "ID" { - t.Fatalf("tb %s tb.PKColumns() is %d not 1, tb.PKColumns()[0].Name is %s not ID", tb.Name, len(tb.PKColumns()), tb.PKColumns()[0].Name) - } - return - } - } - t.Fatal("not table IDSameMapper") -} diff --git a/tag_test.go b/tag_test.go deleted file mode 100644 index 894addac..00000000 --- a/tag_test.go +++ /dev/null @@ -1,600 +0,0 @@ -// Copyright 2017 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "fmt" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "xorm.io/xorm/schemas" -) - -type UserCU struct { - Id int64 - Name string - Created time.Time `xorm:"created"` - Updated time.Time `xorm:"updated"` -} - -func TestCreatedAndUpdated(t *testing.T) { - assert.NoError(t, prepareEngine()) - - u := new(UserCU) - err := testEngine.DropTables(u) - assert.NoError(t, err) - - err = testEngine.CreateTables(u) - assert.NoError(t, err) - - u.Name = "sss" - cnt, err := testEngine.Insert(u) - assert.NoError(t, err) - assert.EqualValues(t, 1, cnt) - - u.Name = "xxx" - cnt, err = testEngine.ID(u.Id).Update(u) - assert.NoError(t, err) - assert.EqualValues(t, 1, cnt) - - u.Id = 0 - u.Created = time.Now().Add(-time.Hour * 24 * 365) - u.Updated = u.Created - cnt, err = testEngine.NoAutoTime().Insert(u) - assert.NoError(t, err) - assert.EqualValues(t, 1, cnt) -} - -type StrangeName struct { - Id_t int64 `xorm:"pk autoincr"` - Name string -} - -func TestStrangeName(t *testing.T) { - assert.NoError(t, prepareEngine()) - - err := testEngine.DropTables(new(StrangeName)) - assert.NoError(t, err) - - err = testEngine.CreateTables(new(StrangeName)) - assert.NoError(t, err) - - _, err = testEngine.Insert(&StrangeName{Name: "sfsfdsfds"}) - assert.NoError(t, err) - - beans := make([]StrangeName, 0) - err = testEngine.Find(&beans) - assert.NoError(t, err) -} - -func TestCreatedUpdated(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type CreatedUpdated struct { - Id int64 - Name string - Value float64 `xorm:"numeric"` - Created time.Time `xorm:"created"` - Created2 time.Time `xorm:"created"` - Updated time.Time `xorm:"updated"` - } - - err := testEngine.Sync2(&CreatedUpdated{}) - assert.NoError(t, err) - - c := &CreatedUpdated{Name: "test"} - _, err = testEngine.Insert(c) - assert.NoError(t, err) - - c2 := new(CreatedUpdated) - has, err := testEngine.ID(c.Id).Get(c2) - assert.NoError(t, err) - - assert.True(t, has) - - c2.Value -= 1 - _, err = testEngine.ID(c2.Id).Update(c2) - assert.NoError(t, err) -} - -func TestCreatedUpdatedInt64(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type CreatedUpdatedInt64 struct { - Id int64 - Name string - Value float64 `xorm:"numeric"` - Created int64 `xorm:"created"` - Created2 int64 `xorm:"created"` - Updated int64 `xorm:"updated"` - } - - assertSync(t, &CreatedUpdatedInt64{}) - - c := &CreatedUpdatedInt64{Name: "test"} - _, err := testEngine.Insert(c) - assert.NoError(t, err) - - c2 := new(CreatedUpdatedInt64) - has, err := testEngine.ID(c.Id).Get(c2) - assert.NoError(t, err) - assert.True(t, has) - - c2.Value -= 1 - _, err = testEngine.ID(c2.Id).Update(c2) - assert.NoError(t, err) -} - -type Lowercase struct { - Id int64 - Name string - ended int64 `xorm:"-"` -} - -func TestLowerCase(t *testing.T) { - assert.NoError(t, prepareEngine()) - - err := testEngine.Sync2(&Lowercase{}) - assert.NoError(t, err) - _, err = testEngine.Where("id > 0").Delete(&Lowercase{}) - assert.NoError(t, err) - - _, err = testEngine.Insert(&Lowercase{ended: 1}) - assert.NoError(t, err) - - ls := make([]Lowercase, 0) - err = testEngine.Find(&ls) - assert.NoError(t, err) - assert.EqualValues(t, 1, len(ls)) -} - -func TestAutoIncrTag(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type TestAutoIncr1 struct { - Id int64 - } - - tb := testEngine.TableInfo(new(TestAutoIncr1)) - cols := tb.Columns() - assert.EqualValues(t, 1, len(cols)) - assert.True(t, cols[0].IsAutoIncrement) - assert.True(t, cols[0].IsPrimaryKey) - assert.Equal(t, "id", cols[0].Name) - - type TestAutoIncr2 struct { - Id int64 `xorm:"id"` - } - - tb = testEngine.TableInfo(new(TestAutoIncr2)) - cols = tb.Columns() - assert.EqualValues(t, 1, len(cols)) - assert.False(t, cols[0].IsAutoIncrement) - assert.False(t, cols[0].IsPrimaryKey) - assert.Equal(t, "id", cols[0].Name) - - type TestAutoIncr3 struct { - Id int64 `xorm:"'ID'"` - } - - tb = testEngine.TableInfo(new(TestAutoIncr3)) - cols = tb.Columns() - assert.EqualValues(t, 1, len(cols)) - assert.False(t, cols[0].IsAutoIncrement) - assert.False(t, cols[0].IsPrimaryKey) - assert.Equal(t, "ID", cols[0].Name) - - type TestAutoIncr4 struct { - Id int64 `xorm:"pk"` - } - - tb = testEngine.TableInfo(new(TestAutoIncr4)) - cols = tb.Columns() - assert.EqualValues(t, 1, len(cols)) - assert.False(t, cols[0].IsAutoIncrement) - assert.True(t, cols[0].IsPrimaryKey) - assert.Equal(t, "id", cols[0].Name) -} - -func TestTagComment(t *testing.T) { - assert.NoError(t, prepareEngine()) - // FIXME: only support mysql - if testEngine.Dialect().DriverName() != schemas.MYSQL { - return - } - - type TestComment1 struct { - Id int64 `xorm:"comment(主键)"` - } - - assert.NoError(t, testEngine.Sync2(new(TestComment1))) - - tables, err := testEngine.DBMetas() - assert.NoError(t, err) - assert.EqualValues(t, 1, len(tables)) - assert.EqualValues(t, 1, len(tables[0].Columns())) - assert.EqualValues(t, "主键", tables[0].Columns()[0].Comment) - - assert.NoError(t, testEngine.DropTables(new(TestComment1))) - - type TestComment2 struct { - Id int64 `xorm:"comment('主键')"` - } - - assert.NoError(t, testEngine.Sync2(new(TestComment2))) - - tables, err = testEngine.DBMetas() - assert.NoError(t, err) - assert.EqualValues(t, 1, len(tables)) - assert.EqualValues(t, 1, len(tables[0].Columns())) - assert.EqualValues(t, "主键", tables[0].Columns()[0].Comment) -} - -func TestTagDefault(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type DefaultStruct struct { - Id int64 - Name string - Age int `xorm:"default(10)"` - } - - assertSync(t, new(DefaultStruct)) - - tables, err := testEngine.DBMetas() - assert.NoError(t, err) - - var defaultVal string - var isDefaultExist bool - tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct") - for _, table := range tables { - if table.Name == tableName { - col := table.GetColumn("age") - assert.NotNil(t, col) - defaultVal = col.Default - isDefaultExist = !col.DefaultIsEmpty - break - } - } - assert.True(t, isDefaultExist) - assert.EqualValues(t, "10", defaultVal) - - cnt, err := testEngine.Omit("age").Insert(&DefaultStruct{ - Name: "test", - Age: 20, - }) - assert.NoError(t, err) - assert.EqualValues(t, 1, cnt) - - var s DefaultStruct - has, err := testEngine.ID(1).Get(&s) - assert.NoError(t, err) - assert.True(t, has) - assert.EqualValues(t, 10, s.Age) - assert.EqualValues(t, "test", s.Name) -} - -func TestTagDefault2(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type DefaultStruct2 struct { - Id int64 - Name string - } - - assertSync(t, new(DefaultStruct2)) - - tables, err := testEngine.DBMetas() - assert.NoError(t, err) - - var defaultVal string - var isDefaultExist bool - tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct2") - for _, table := range tables { - if table.Name == tableName { - col := table.GetColumn("name") - assert.NotNil(t, col) - defaultVal = col.Default - isDefaultExist = !col.DefaultIsEmpty - break - } - } - assert.False(t, isDefaultExist, fmt.Sprintf("default value is --%v--", defaultVal)) - assert.EqualValues(t, "", defaultVal) -} - -func TestTagDefault3(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type DefaultStruct3 struct { - Id int64 - Name string `xorm:"default('myname')"` - } - - assertSync(t, new(DefaultStruct3)) - - tables, err := testEngine.DBMetas() - assert.NoError(t, err) - - var defaultVal string - var isDefaultExist bool - tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct3") - for _, table := range tables { - if table.Name == tableName { - col := table.GetColumn("name") - assert.NotNil(t, col) - defaultVal = col.Default - isDefaultExist = !col.DefaultIsEmpty - break - } - } - assert.True(t, isDefaultExist) - assert.EqualValues(t, "'myname'", defaultVal) -} - -func TestTagDefault4(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type DefaultStruct4 struct { - Id int64 - Created time.Time `xorm:"default(CURRENT_TIMESTAMP)"` - } - - assertSync(t, new(DefaultStruct4)) - - tables, err := testEngine.DBMetas() - assert.NoError(t, err) - - var defaultVal string - var isDefaultExist bool - tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct4") - for _, table := range tables { - if table.Name == tableName { - col := table.GetColumn("created") - assert.NotNil(t, col) - defaultVal = col.Default - isDefaultExist = !col.DefaultIsEmpty - break - } - } - assert.True(t, isDefaultExist) - assert.True(t, "CURRENT_TIMESTAMP" == defaultVal || - "now()" == defaultVal || - "getdate" == defaultVal, defaultVal) -} - -func TestTagDefault5(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type DefaultStruct5 struct { - Id int64 - Created time.Time `xorm:"default('2006-01-02 15:04:05')"` - } - - assertSync(t, new(DefaultStruct5)) - table := testEngine.TableInfo(new(DefaultStruct5)) - createdCol := table.GetColumn("created") - assert.NotNil(t, createdCol) - assert.EqualValues(t, "'2006-01-02 15:04:05'", createdCol.Default) - assert.False(t, createdCol.DefaultIsEmpty) - - tables, err := testEngine.DBMetas() - assert.NoError(t, err) - - var defaultVal string - var isDefaultExist bool - tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct5") - for _, table := range tables { - if table.Name == tableName { - col := table.GetColumn("created") - assert.NotNil(t, col) - defaultVal = col.Default - isDefaultExist = !col.DefaultIsEmpty - break - } - } - assert.True(t, isDefaultExist) - assert.EqualValues(t, "'2006-01-02 15:04:05'", defaultVal) -} - -func TestTagDefault6(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type DefaultStruct6 struct { - Id int64 - IsMan bool `xorm:"default(true)"` - } - - assertSync(t, new(DefaultStruct6)) - - tables, err := testEngine.DBMetas() - assert.NoError(t, err) - - var defaultVal string - var isDefaultExist bool - tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct6") - for _, table := range tables { - if table.Name == tableName { - col := table.GetColumn("is_man") - assert.NotNil(t, col) - defaultVal = col.Default - isDefaultExist = !col.DefaultIsEmpty - break - } - } - assert.True(t, isDefaultExist) - if defaultVal == "1" { - defaultVal = "true" - } else if defaultVal == "0" { - defaultVal = "false" - } - assert.EqualValues(t, "true", defaultVal) -} - -func TestTagsDirection(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type OnlyFromDBStruct struct { - Id int64 - Name string - Uuid string `xorm:"<- default '1'"` - } - - assertSync(t, new(OnlyFromDBStruct)) - - cnt, err := testEngine.Insert(&OnlyFromDBStruct{ - Name: "test", - Uuid: "2", - }) - assert.NoError(t, err) - assert.EqualValues(t, 1, cnt) - - var s OnlyFromDBStruct - has, err := testEngine.ID(1).Get(&s) - assert.NoError(t, err) - assert.True(t, has) - assert.EqualValues(t, "1", s.Uuid) - assert.EqualValues(t, "test", s.Name) - - cnt, err = testEngine.ID(1).Update(&OnlyFromDBStruct{ - Uuid: "3", - Name: "test1", - }) - assert.NoError(t, err) - assert.EqualValues(t, 1, cnt) - - var s3 OnlyFromDBStruct - has, err = testEngine.ID(1).Get(&s3) - assert.NoError(t, err) - assert.True(t, has) - assert.EqualValues(t, "1", s3.Uuid) - assert.EqualValues(t, "test1", s3.Name) - - type OnlyToDBStruct struct { - Id int64 - Name string - Uuid string `xorm:"->"` - } - - assertSync(t, new(OnlyToDBStruct)) - - cnt, err = testEngine.Insert(&OnlyToDBStruct{ - Name: "test", - Uuid: "2", - }) - assert.NoError(t, err) - assert.EqualValues(t, 1, cnt) - - var s2 OnlyToDBStruct - has, err = testEngine.ID(1).Get(&s2) - assert.NoError(t, err) - assert.True(t, has) - assert.EqualValues(t, "", s2.Uuid) - assert.EqualValues(t, "test", s2.Name) -} - -func TestTagTime(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type TagUTCStruct struct { - Id int64 - Name string - Created time.Time `xorm:"created utc"` - } - - assertSync(t, new(TagUTCStruct)) - - assert.EqualValues(t, time.Local.String(), testEngine.GetTZLocation().String()) - - s := TagUTCStruct{ - Name: "utc", - } - cnt, err := testEngine.Insert(&s) - assert.NoError(t, err) - assert.EqualValues(t, 1, cnt) - - var u TagUTCStruct - has, err := testEngine.ID(1).Get(&u) - assert.NoError(t, err) - assert.True(t, has) - assert.EqualValues(t, s.Created.Format("2006-01-02 15:04:05"), u.Created.Format("2006-01-02 15:04:05")) - - var tm string - has, err = testEngine.Table("tag_u_t_c_struct").Cols("created").Get(&tm) - assert.NoError(t, err) - assert.True(t, has) - assert.EqualValues(t, s.Created.UTC().Format("2006-01-02 15:04:05"), - strings.Replace(strings.Replace(tm, "T", " ", -1), "Z", "", -1)) -} - -func TestSplitTag(t *testing.T) { - var cases = []struct { - tag string - tags []string - }{ - {"not null default '2000-01-01 00:00:00' TIMESTAMP", []string{"not", "null", "default", "'2000-01-01 00:00:00'", "TIMESTAMP"}}, - {"TEXT", []string{"TEXT"}}, - {"default('2000-01-01 00:00:00')", []string{"default('2000-01-01 00:00:00')"}}, - {"json binary", []string{"json", "binary"}}, - } - - for _, kase := range cases { - tags := splitTag(kase.tag) - if !sliceEq(tags, kase.tags) { - t.Fatalf("[%d]%v is not equal [%d]%v", len(tags), tags, len(kase.tags), kase.tags) - } - } -} - -func TestTagAutoIncr(t *testing.T) { - assert.NoError(t, prepareEngine()) - - type TagAutoIncr struct { - Id int64 - Name string - } - - assertSync(t, new(TagAutoIncr)) - - tables, err := testEngine.DBMetas() - assert.NoError(t, err) - assert.EqualValues(t, 1, len(tables)) - assert.EqualValues(t, tableMapper.Obj2Table("TagAutoIncr"), tables[0].Name) - col := tables[0].GetColumn(colMapper.Obj2Table("Id")) - assert.NotNil(t, col) - assert.True(t, col.IsPrimaryKey) - assert.True(t, col.IsAutoIncrement) - - col2 := tables[0].GetColumn(colMapper.Obj2Table("Name")) - assert.NotNil(t, col2) - assert.False(t, col2.IsPrimaryKey) - assert.False(t, col2.IsAutoIncrement) -} - -func TestTagPrimarykey(t *testing.T) { - assert.NoError(t, prepareEngine()) - type TagPrimaryKey struct { - Id int64 `xorm:"pk"` - Name string `xorm:"VARCHAR(20) pk"` - } - - assertSync(t, new(TagPrimaryKey)) - - tables, err := testEngine.DBMetas() - assert.NoError(t, err) - assert.EqualValues(t, 1, len(tables)) - assert.EqualValues(t, tableMapper.Obj2Table("TagPrimaryKey"), tables[0].Name) - col := tables[0].GetColumn(colMapper.Obj2Table("Id")) - assert.NotNil(t, col) - assert.True(t, col.IsPrimaryKey) - assert.False(t, col.IsAutoIncrement) - - col2 := tables[0].GetColumn(colMapper.Obj2Table("Name")) - assert.NotNil(t, col2) - assert.True(t, col2.IsPrimaryKey) - assert.False(t, col2.IsAutoIncrement) -} diff --git a/tag_version_test.go b/tag_version_test.go deleted file mode 100644 index 878737c1..00000000 --- a/tag_version_test.go +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright 2017 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "errors" - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -type VersionS struct { - Id int64 - Name string - Ver int `xorm:"version"` - Created time.Time `xorm:"created"` -} - -func TestVersion1(t *testing.T) { - assert.NoError(t, prepareEngine()) - - err := testEngine.DropTables(new(VersionS)) - if err != nil { - t.Error(err) - panic(err) - } - - err = testEngine.CreateTables(new(VersionS)) - if err != nil { - t.Error(err) - panic(err) - } - - ver := &VersionS{Name: "sfsfdsfds"} - _, err = testEngine.Insert(ver) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(ver) - if ver.Ver != 1 { - err = errors.New("insert error") - t.Error(err) - panic(err) - } - - newVer := new(VersionS) - has, err := testEngine.ID(ver.Id).Get(newVer) - if err != nil { - t.Error(err) - panic(err) - } - - if !has { - t.Error(fmt.Errorf("no version id is %v", ver.Id)) - panic(err) - } - fmt.Println(newVer) - if newVer.Ver != 1 { - err = errors.New("insert error") - t.Error(err) - panic(err) - } - - newVer.Name = "-------" - _, err = testEngine.ID(ver.Id).Update(newVer) - if err != nil { - t.Error(err) - panic(err) - } - if newVer.Ver != 2 { - err = errors.New("update should set version back to struct") - t.Error(err) - } - - newVer = new(VersionS) - has, err = testEngine.ID(ver.Id).Get(newVer) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(newVer) - if newVer.Ver != 2 { - err = errors.New("update error") - t.Error(err) - panic(err) - } -} - -func TestVersion2(t *testing.T) { - assert.NoError(t, prepareEngine()) - - err := testEngine.DropTables(new(VersionS)) - if err != nil { - t.Error(err) - panic(err) - } - - err = testEngine.CreateTables(new(VersionS)) - if err != nil { - t.Error(err) - panic(err) - } - - var vers = []VersionS{ - {Name: "sfsfdsfds"}, - {Name: "xxxxx"}, - } - _, err = testEngine.Insert(vers) - if err != nil { - t.Error(err) - panic(err) - } - - fmt.Println(vers) - - for _, v := range vers { - if v.Ver != 1 { - err := errors.New("version should be 1") - t.Error(err) - panic(err) - } - } -} - -type VersionUintS struct { - Id int64 - Name string - Ver uint `xorm:"version"` - Created time.Time `xorm:"created"` -} - -func TestVersion3(t *testing.T) { - assert.NoError(t, prepareEngine()) - - err := testEngine.DropTables(new(VersionUintS)) - if err != nil { - t.Error(err) - panic(err) - } - - err = testEngine.CreateTables(new(VersionUintS)) - if err != nil { - t.Error(err) - panic(err) - } - - ver := &VersionUintS{Name: "sfsfdsfds"} - _, err = testEngine.Insert(ver) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(ver) - if ver.Ver != 1 { - err = errors.New("insert error") - t.Error(err) - panic(err) - } - - newVer := new(VersionUintS) - has, err := testEngine.ID(ver.Id).Get(newVer) - if err != nil { - t.Error(err) - panic(err) - } - - if !has { - t.Error(fmt.Errorf("no version id is %v", ver.Id)) - panic(err) - } - fmt.Println(newVer) - if newVer.Ver != 1 { - err = errors.New("insert error") - t.Error(err) - panic(err) - } - - newVer.Name = "-------" - _, err = testEngine.ID(ver.Id).Update(newVer) - if err != nil { - t.Error(err) - panic(err) - } - if newVer.Ver != 2 { - err = errors.New("update should set version back to struct") - t.Error(err) - } - - newVer = new(VersionUintS) - has, err = testEngine.ID(ver.Id).Get(newVer) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(newVer) - if newVer.Ver != 2 { - err = errors.New("update error") - t.Error(err) - panic(err) - } -} - -func TestVersion4(t *testing.T) { - assert.NoError(t, prepareEngine()) - - err := testEngine.DropTables(new(VersionUintS)) - if err != nil { - t.Error(err) - panic(err) - } - - err = testEngine.CreateTables(new(VersionUintS)) - if err != nil { - t.Error(err) - panic(err) - } - - var vers = []VersionUintS{ - {Name: "sfsfdsfds"}, - {Name: "xxxxx"}, - } - _, err = testEngine.Insert(vers) - if err != nil { - t.Error(err) - panic(err) - } - - fmt.Println(vers) - - for _, v := range vers { - if v.Ver != 1 { - err := errors.New("version should be 1") - t.Error(err) - panic(err) - } - } -} diff --git a/tags/parser.go b/tags/parser.go new file mode 100644 index 00000000..15dcaa30 --- /dev/null +++ b/tags/parser.go @@ -0,0 +1,239 @@ +// Copyright 2020 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tags + +import ( + "errors" + "fmt" + "reflect" + "strings" + "time" + + "xorm.io/xorm/caches" + "xorm.io/xorm/convert" + "xorm.io/xorm/dialects" + "xorm.io/xorm/names" + "xorm.io/xorm/schemas" +) + +type Parser struct { + identifier string + dialect dialects.Dialect + ColumnMapper names.Mapper + TableMapper names.Mapper + handlers map[string]Handler + cacherMgr *caches.Manager +} + +func NewParser(identifier string, dialect dialects.Dialect, tableMapper, columnMapper names.Mapper, cacherMgr *caches.Manager) *Parser { + return &Parser{ + identifier: identifier, + dialect: dialect, + TableMapper: tableMapper, + ColumnMapper: columnMapper, + handlers: defaultTagHandlers, + cacherMgr: cacherMgr, + } +} + +func addIndex(indexName string, table *schemas.Table, col *schemas.Column, indexType int) { + if index, ok := table.Indexes[indexName]; ok { + index.AddColumn(col.Name) + col.Indexes[index.Name] = indexType + } else { + index := schemas.NewIndex(indexName, indexType) + index.AddColumn(col.Name) + table.AddIndex(index) + col.Indexes[index.Name] = indexType + } +} + +func (parser *Parser) MapType(v reflect.Value) (*schemas.Table, error) { + t := v.Type() + table := schemas.NewEmptyTable() + table.Type = t + table.Name = names.GetTableName(parser.TableMapper, v) + + var idFieldColName string + var hasCacheTag, hasNoCacheTag bool + + for i := 0; i < t.NumField(); i++ { + tag := t.Field(i).Tag + + ormTagStr := tag.Get(parser.identifier) + var col *schemas.Column + fieldValue := v.Field(i) + fieldType := fieldValue.Type() + + if ormTagStr != "" { + col = &schemas.Column{ + FieldName: t.Field(i).Name, + Nullable: true, + IsPrimaryKey: false, + IsAutoIncrement: false, + MapType: schemas.TWOSIDES, + Indexes: make(map[string]int), + DefaultIsEmpty: true, + } + tags := splitTag(ormTagStr) + + if len(tags) > 0 { + if tags[0] == "-" { + continue + } + + var ctx = Context{ + table: table, + col: col, + fieldValue: fieldValue, + indexNames: make(map[string]int), + parser: parser, + } + + if strings.HasPrefix(strings.ToUpper(tags[0]), "EXTENDS") { + pStart := strings.Index(tags[0], "(") + if pStart > -1 && strings.HasSuffix(tags[0], ")") { + var tagPrefix = strings.TrimFunc(tags[0][pStart+1:len(tags[0])-1], func(r rune) bool { + return r == '\'' || r == '"' + }) + + ctx.params = []string{tagPrefix} + } + + if err := ExtendsTagHandler(&ctx); err != nil { + return nil, err + } + continue + } + + for j, key := range tags { + if ctx.ignoreNext { + ctx.ignoreNext = false + continue + } + + k := strings.ToUpper(key) + ctx.tagName = k + ctx.params = []string{} + + pStart := strings.Index(k, "(") + if pStart == 0 { + return nil, errors.New("( could not be the first character") + } + if pStart > -1 { + if !strings.HasSuffix(k, ")") { + return nil, fmt.Errorf("field %s tag %s cannot match ) character", col.FieldName, key) + } + + ctx.tagName = k[:pStart] + ctx.params = strings.Split(key[pStart+1:len(k)-1], ",") + } + + if j > 0 { + ctx.preTag = strings.ToUpper(tags[j-1]) + } + if j < len(tags)-1 { + ctx.nextTag = tags[j+1] + } else { + ctx.nextTag = "" + } + + if h, ok := parser.handlers[ctx.tagName]; ok { + if err := h(&ctx); err != nil { + return nil, err + } + } else { + if strings.HasPrefix(key, "'") && strings.HasSuffix(key, "'") { + col.Name = key[1 : len(key)-1] + } else { + col.Name = key + } + } + + if ctx.hasCacheTag { + hasCacheTag = true + } + if ctx.hasNoCacheTag { + hasNoCacheTag = true + } + } + + if col.SQLType.Name == "" { + col.SQLType = schemas.Type2SQLType(fieldType) + } + parser.dialect.SQLType(col) + if col.Length == 0 { + col.Length = col.SQLType.DefaultLength + } + if col.Length2 == 0 { + col.Length2 = col.SQLType.DefaultLength2 + } + if col.Name == "" { + col.Name = parser.ColumnMapper.Obj2Table(t.Field(i).Name) + } + + if ctx.isUnique { + ctx.indexNames[col.Name] = schemas.UniqueType + } else if ctx.isIndex { + ctx.indexNames[col.Name] = schemas.IndexType + } + + for indexName, indexType := range ctx.indexNames { + addIndex(indexName, table, col, indexType) + } + } + } else { + var sqlType schemas.SQLType + if fieldValue.CanAddr() { + if _, ok := fieldValue.Addr().Interface().(convert.Conversion); ok { + sqlType = schemas.SQLType{Name: schemas.Text} + } + } + if _, ok := fieldValue.Interface().(convert.Conversion); ok { + sqlType = schemas.SQLType{Name: schemas.Text} + } else { + sqlType = schemas.Type2SQLType(fieldType) + } + col = schemas.NewColumn(parser.ColumnMapper.Obj2Table(t.Field(i).Name), + t.Field(i).Name, sqlType, sqlType.DefaultLength, + sqlType.DefaultLength2, true) + + if fieldType.Kind() == reflect.Int64 && (strings.ToUpper(col.FieldName) == "ID" || strings.HasSuffix(strings.ToUpper(col.FieldName), ".ID")) { + idFieldColName = col.Name + } + } + if col.IsAutoIncrement { + col.Nullable = false + } + + table.AddColumn(col) + + } // end for + + if idFieldColName != "" && len(table.PrimaryKeys) == 0 { + col := table.GetColumn(idFieldColName) + col.IsPrimaryKey = true + col.IsAutoIncrement = true + col.Nullable = false + table.PrimaryKeys = append(table.PrimaryKeys, col.Name) + table.AutoIncrement = col.Name + } + + if hasCacheTag { + if parser.cacherMgr.GetDefaultCacher() != nil { // !nash! use engine's cacher if provided + //engine.logger.Info("enable cache on table:", table.Name) + parser.cacherMgr.SetCacher(table.Name, parser.cacherMgr.GetDefaultCacher()) + } else { + //engine.logger.Info("enable LRU cache on table:", table.Name) + parser.cacherMgr.SetCacher(table.Name, caches.NewLRUCacher2(caches.NewMemoryStore(), time.Hour, 10000)) + } + } + if hasNoCacheTag { + //engine.logger.Info("disable cache on table:", table.Name) + parser.cacherMgr.SetCacher(table.Name, nil) + } + + return table, nil +} diff --git a/tag.go b/tags/tag.go similarity index 83% rename from tag.go rename to tags/tag.go index 92fcdb5e..3222615a 100644 --- a/tag.go +++ b/tags/tag.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package xorm +package tags import ( "fmt" @@ -34,8 +34,8 @@ func splitTag(tag string) (tags []string) { return } -// tagContext represents a context for xorm tag parse. -type tagContext struct { +// Context represents a context for xorm tag parse. +type Context struct { tagName string params []string preTag, nextTag string @@ -45,18 +45,18 @@ type tagContext struct { isIndex bool isUnique bool indexNames map[string]int - engine *Engine + parser *Parser hasCacheTag bool hasNoCacheTag bool ignoreNext bool } -// tagHandler describes tag handler for XORM -type tagHandler func(ctx *tagContext) error +// Handler describes tag handler for XORM +type Handler func(ctx *Context) error var ( // defaultTagHandlers enumerates all the default tag handler - defaultTagHandlers = map[string]tagHandler{ + defaultTagHandlers = map[string]Handler{ "<-": OnlyFromDBTagHandler, "->": OnlyToDBTagHandler, "PK": PKTagHandler, @@ -86,43 +86,43 @@ func init() { } // IgnoreTagHandler describes ignored tag handler -func IgnoreTagHandler(ctx *tagContext) error { +func IgnoreTagHandler(ctx *Context) error { return nil } // OnlyFromDBTagHandler describes mapping direction tag handler -func OnlyFromDBTagHandler(ctx *tagContext) error { +func OnlyFromDBTagHandler(ctx *Context) error { ctx.col.MapType = schemas.ONLYFROMDB return nil } // OnlyToDBTagHandler describes mapping direction tag handler -func OnlyToDBTagHandler(ctx *tagContext) error { +func OnlyToDBTagHandler(ctx *Context) error { ctx.col.MapType = schemas.ONLYTODB return nil } // PKTagHandler describes primary key tag handler -func PKTagHandler(ctx *tagContext) error { +func PKTagHandler(ctx *Context) error { ctx.col.IsPrimaryKey = true ctx.col.Nullable = false return nil } // NULLTagHandler describes null tag handler -func NULLTagHandler(ctx *tagContext) error { +func NULLTagHandler(ctx *Context) error { ctx.col.Nullable = (strings.ToUpper(ctx.preTag) != "NOT") return nil } // NotNullTagHandler describes notnull tag handler -func NotNullTagHandler(ctx *tagContext) error { +func NotNullTagHandler(ctx *Context) error { ctx.col.Nullable = false return nil } // AutoIncrTagHandler describes autoincr tag handler -func AutoIncrTagHandler(ctx *tagContext) error { +func AutoIncrTagHandler(ctx *Context) error { ctx.col.IsAutoIncrement = true /* if len(ctx.params) > 0 { @@ -139,7 +139,7 @@ func AutoIncrTagHandler(ctx *tagContext) error { } // DefaultTagHandler describes default tag handler -func DefaultTagHandler(ctx *tagContext) error { +func DefaultTagHandler(ctx *Context) error { if len(ctx.params) > 0 { ctx.col.Default = ctx.params[0] } else { @@ -151,26 +151,26 @@ func DefaultTagHandler(ctx *tagContext) error { } // CreatedTagHandler describes created tag handler -func CreatedTagHandler(ctx *tagContext) error { +func CreatedTagHandler(ctx *Context) error { ctx.col.IsCreated = true return nil } // VersionTagHandler describes version tag handler -func VersionTagHandler(ctx *tagContext) error { +func VersionTagHandler(ctx *Context) error { ctx.col.IsVersion = true ctx.col.Default = "1" return nil } // UTCTagHandler describes utc tag handler -func UTCTagHandler(ctx *tagContext) error { +func UTCTagHandler(ctx *Context) error { ctx.col.TimeZone = time.UTC return nil } // LocalTagHandler describes local tag handler -func LocalTagHandler(ctx *tagContext) error { +func LocalTagHandler(ctx *Context) error { if len(ctx.params) == 0 { ctx.col.TimeZone = time.Local } else { @@ -184,19 +184,19 @@ func LocalTagHandler(ctx *tagContext) error { } // UpdatedTagHandler describes updated tag handler -func UpdatedTagHandler(ctx *tagContext) error { +func UpdatedTagHandler(ctx *Context) error { ctx.col.IsUpdated = true return nil } // DeletedTagHandler describes deleted tag handler -func DeletedTagHandler(ctx *tagContext) error { +func DeletedTagHandler(ctx *Context) error { ctx.col.IsDeleted = true return nil } // IndexTagHandler describes index tag handler -func IndexTagHandler(ctx *tagContext) error { +func IndexTagHandler(ctx *Context) error { if len(ctx.params) > 0 { ctx.indexNames[ctx.params[0]] = schemas.IndexType } else { @@ -206,7 +206,7 @@ func IndexTagHandler(ctx *tagContext) error { } // UniqueTagHandler describes unique tag handler -func UniqueTagHandler(ctx *tagContext) error { +func UniqueTagHandler(ctx *Context) error { if len(ctx.params) > 0 { ctx.indexNames[ctx.params[0]] = schemas.UniqueType } else { @@ -216,7 +216,7 @@ func UniqueTagHandler(ctx *tagContext) error { } // CommentTagHandler add comment to column -func CommentTagHandler(ctx *tagContext) error { +func CommentTagHandler(ctx *Context) error { if len(ctx.params) > 0 { ctx.col.Comment = strings.Trim(ctx.params[0], "' ") } @@ -224,7 +224,7 @@ func CommentTagHandler(ctx *tagContext) error { } // SQLTypeTagHandler describes SQL Type tag handler -func SQLTypeTagHandler(ctx *tagContext) error { +func SQLTypeTagHandler(ctx *Context) error { ctx.col.SQLType = schemas.SQLType{Name: ctx.tagName} if len(ctx.params) > 0 { if ctx.tagName == schemas.Enum { @@ -264,7 +264,7 @@ func SQLTypeTagHandler(ctx *tagContext) error { } // ExtendsTagHandler describes extends tag handler -func ExtendsTagHandler(ctx *tagContext) error { +func ExtendsTagHandler(ctx *Context) error { var fieldValue = ctx.fieldValue var isPtr = false switch fieldValue.Kind() { @@ -280,7 +280,7 @@ func ExtendsTagHandler(ctx *tagContext) error { isPtr = true fallthrough case reflect.Struct: - parentTable, err := ctx.engine.mapType(fieldValue) + parentTable, err := ctx.parser.MapType(fieldValue) if err != nil { return err } @@ -316,7 +316,7 @@ func ExtendsTagHandler(ctx *tagContext) error { } // CacheTagHandler describes cache tag handler -func CacheTagHandler(ctx *tagContext) error { +func CacheTagHandler(ctx *Context) error { if !ctx.hasCacheTag { ctx.hasCacheTag = true } @@ -324,7 +324,7 @@ func CacheTagHandler(ctx *tagContext) error { } // NoCacheTagHandler describes nocache tag handler -func NoCacheTagHandler(ctx *tagContext) error { +func NoCacheTagHandler(ctx *Context) error { if !ctx.hasNoCacheTag { ctx.hasNoCacheTag = true } diff --git a/tags/tag_test.go b/tags/tag_test.go new file mode 100644 index 00000000..5775b40a --- /dev/null +++ b/tags/tag_test.go @@ -0,0 +1,30 @@ +// Copyright 2017 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tags + +import ( + "testing" + + "xorm.io/xorm/internal/utils" +) + +func TestSplitTag(t *testing.T) { + var cases = []struct { + tag string + tags []string + }{ + {"not null default '2000-01-01 00:00:00' TIMESTAMP", []string{"not", "null", "default", "'2000-01-01 00:00:00'", "TIMESTAMP"}}, + {"TEXT", []string{"TEXT"}}, + {"default('2000-01-01 00:00:00')", []string{"default('2000-01-01 00:00:00')"}}, + {"json binary", []string{"json", "binary"}}, + } + + for _, kase := range cases { + tags := splitTag(kase.tag) + if !utils.SliceEq(tags, kase.tags) { + t.Fatalf("[%d]%v is not equal [%d]%v", len(tags), tags, len(kase.tags), kase.tags) + } + } +} diff --git a/tags_test.go b/tags_test.go new file mode 100644 index 00000000..2d90948b --- /dev/null +++ b/tags_test.go @@ -0,0 +1,1501 @@ +// Copyright 2017 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "errors" + "fmt" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "xorm.io/xorm/names" + "xorm.io/xorm/schemas" +) + +type tempUser struct { + Id int64 + Username string +} + +type tempUser2 struct { + TempUser tempUser `xorm:"extends"` + Departname string +} + +type tempUser3 struct { + Temp *tempUser `xorm:"extends"` + Departname string +} + +type tempUser4 struct { + TempUser2 tempUser2 `xorm:"extends"` +} + +type Userinfo struct { + Uid int64 `xorm:"id pk not null autoincr"` + Username string `xorm:"unique"` + Departname string + Alias string `xorm:"-"` + Created time.Time + Detail Userdetail `xorm:"detail_id int(11)"` + Height float64 + Avatar []byte + IsMan bool +} + +type Userdetail struct { + Id int64 + Intro string `xorm:"text"` + Profile string `xorm:"varchar(2000)"` +} + +type UserAndDetail struct { + Userinfo `xorm:"extends"` + Userdetail `xorm:"extends"` +} + +func TestExtends(t *testing.T) { + assert.NoError(t, prepareEngine()) + + err := testEngine.DropTables(&tempUser2{}) + assert.NoError(t, err) + + err = testEngine.CreateTables(&tempUser2{}) + assert.NoError(t, err) + + tu := &tempUser2{tempUser{0, "extends"}, "dev depart"} + _, err = testEngine.Insert(tu) + assert.NoError(t, err) + + tu2 := &tempUser2{} + _, err = testEngine.Get(tu2) + assert.NoError(t, err) + + tu3 := &tempUser2{tempUser{0, "extends update"}, ""} + _, err = testEngine.ID(tu2.TempUser.Id).Update(tu3) + assert.NoError(t, err) + + err = testEngine.DropTables(&tempUser4{}) + assert.NoError(t, err) + + err = testEngine.CreateTables(&tempUser4{}) + assert.NoError(t, err) + + tu8 := &tempUser4{tempUser2{tempUser{0, "extends"}, "dev depart"}} + _, err = testEngine.Insert(tu8) + assert.NoError(t, err) + + tu9 := &tempUser4{} + _, err = testEngine.Get(tu9) + assert.NoError(t, err) + + if tu9.TempUser2.TempUser.Username != tu8.TempUser2.TempUser.Username || tu9.TempUser2.Departname != tu8.TempUser2.Departname { + err = errors.New(fmt.Sprintln("not equal for", tu8, tu9)) + t.Error(err) + panic(err) + } + + tu10 := &tempUser4{tempUser2{tempUser{0, "extends update"}, ""}} + _, err = testEngine.ID(tu9.TempUser2.TempUser.Id).Update(tu10) + assert.NoError(t, err) + + err = testEngine.DropTables(&tempUser3{}) + assert.NoError(t, err) + + err = testEngine.CreateTables(&tempUser3{}) + assert.NoError(t, err) + + tu4 := &tempUser3{&tempUser{0, "extends"}, "dev depart"} + _, err = testEngine.Insert(tu4) + assert.NoError(t, err) + + tu5 := &tempUser3{} + _, err = testEngine.Get(tu5) + assert.NoError(t, err) + + if tu5.Temp == nil { + err = errors.New("error get data extends") + t.Error(err) + panic(err) + } + if tu5.Temp.Id != 1 || tu5.Temp.Username != "extends" || + tu5.Departname != "dev depart" { + err = errors.New("error get data extends") + t.Error(err) + panic(err) + } + + tu6 := &tempUser3{&tempUser{0, "extends update"}, ""} + _, err = testEngine.ID(tu5.Temp.Id).Update(tu6) + assert.NoError(t, err) + + users := make([]tempUser3, 0) + err = testEngine.Find(&users) + assert.NoError(t, err) + assert.EqualValues(t, 1, len(users), "error get data not 1") + + assertSync(t, new(Userinfo), new(Userdetail)) + + detail := Userdetail{ + Intro: "I'm in China", + } + _, err = testEngine.Insert(&detail) + assert.NoError(t, err) + + _, err = testEngine.Insert(&Userinfo{ + Username: "lunny", + Detail: detail, + }) + assert.NoError(t, err) + + var info UserAndDetail + qt := testEngine.Quote + ui := testEngine.TableName(new(Userinfo), true) + ud := testEngine.TableName(&detail, true) + uiid := testEngine.GetColumnMapper().Obj2Table("Id") + udid := "detail_id" + sql := fmt.Sprintf("select * from %s, %s where %s.%s = %s.%s", + qt(ui), qt(ud), qt(ui), qt(udid), qt(ud), qt(uiid)) + b, err := testEngine.SQL(sql).NoCascade().Get(&info) + assert.NoError(t, err) + if !b { + err = errors.New("should has lest one record") + t.Error(err) + panic(err) + } + fmt.Println(info) + if info.Userinfo.Uid == 0 || info.Userdetail.Id == 0 { + err = errors.New("all of the id should has value") + t.Error(err) + panic(err) + } + + fmt.Println("----join--info2") + var info2 UserAndDetail + b, err = testEngine.Table(&Userinfo{}). + Join("LEFT", qt(ud), qt(ui)+"."+qt("detail_id")+" = "+qt(ud)+"."+qt(uiid)). + NoCascade().Get(&info2) + if err != nil { + t.Error(err) + panic(err) + } + if !b { + err = errors.New("should has lest one record") + t.Error(err) + panic(err) + } + if info2.Userinfo.Uid == 0 || info2.Userdetail.Id == 0 { + err = errors.New("all of the id should has value") + t.Error(err) + panic(err) + } + fmt.Println(info2) + + fmt.Println("----join--infos2") + var infos2 = make([]UserAndDetail, 0) + err = testEngine.Table(&Userinfo{}). + Join("LEFT", qt(ud), qt(ui)+"."+qt("detail_id")+" = "+qt(ud)+"."+qt(uiid)). + NoCascade(). + Find(&infos2) + assert.NoError(t, err) + fmt.Println(infos2) +} + +type MessageBase struct { + Id int64 `xorm:"int(11) pk autoincr"` + TypeId int64 `xorm:"int(11) notnull"` +} + +type Message struct { + MessageBase `xorm:"extends"` + Title string `xorm:"varchar(100) notnull"` + Content string `xorm:"text notnull"` + Uid int64 `xorm:"int(11) notnull"` + ToUid int64 `xorm:"int(11) notnull"` + CreateTime time.Time `xorm:"datetime notnull created"` +} + +type MessageUser struct { + Id int64 + Name string +} + +type MessageType struct { + Id int64 + Name string +} + +type MessageExtend3 struct { + Message `xorm:"extends"` + Sender MessageUser `xorm:"extends"` + Receiver MessageUser `xorm:"extends"` + Type MessageType `xorm:"extends"` +} + +type MessageExtend4 struct { + Message `xorm:"extends"` + MessageUser `xorm:"extends"` + MessageType `xorm:"extends"` +} + +func TestExtends2(t *testing.T) { + assert.NoError(t, prepareEngine()) + + err := testEngine.DropTables(&Message{}, &MessageUser{}, &MessageType{}) + assert.NoError(t, err) + + err = testEngine.CreateTables(&Message{}, &MessageUser{}, &MessageType{}) + assert.NoError(t, err) + + var sender = MessageUser{Name: "sender"} + var receiver = MessageUser{Name: "receiver"} + var msgtype = MessageType{Name: "type"} + _, err = testEngine.Insert(&sender, &receiver, &msgtype) + assert.NoError(t, err) + + msg := Message{ + MessageBase: MessageBase{ + Id: msgtype.Id, + }, + Title: "test", + Content: "test", + Uid: sender.Id, + ToUid: receiver.Id, + } + + session := testEngine.NewSession() + defer session.Close() + + // MSSQL deny insert identity column excep declare as below + if testEngine.Dialect().DBType() == schemas.MSSQL { + err = session.Begin() + assert.NoError(t, err) + _, err = session.Exec("SET IDENTITY_INSERT message ON") + assert.NoError(t, err) + } + cnt, err := session.Insert(&msg) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + if testEngine.Dialect().DBType() == schemas.MSSQL { + err = session.Commit() + assert.NoError(t, err) + } + + var mapper = testEngine.GetTableMapper().Obj2Table + var quote = testEngine.Quote + userTableName := quote(testEngine.TableName(mapper("MessageUser"), true)) + typeTableName := quote(testEngine.TableName(mapper("MessageType"), true)) + msgTableName := quote(testEngine.TableName(mapper("Message"), true)) + + list := make([]Message, 0) + err = session.Table(msgTableName).Join("LEFT", []string{userTableName, "sender"}, "`sender`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Uid")+"`"). + Join("LEFT", []string{userTableName, "receiver"}, "`receiver`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("ToUid")+"`"). + Join("LEFT", []string{typeTableName, "type"}, "`type`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Id")+"`"). + Find(&list) + assert.NoError(t, err) + + assert.EqualValues(t, 1, len(list), fmt.Sprintln("should have 1 message, got", len(list))) + assert.EqualValues(t, msg.Id, list[0].Id, fmt.Sprintln("should message equal", list[0], msg)) +} + +func TestExtends3(t *testing.T) { + assert.NoError(t, prepareEngine()) + + err := testEngine.DropTables(&Message{}, &MessageUser{}, &MessageType{}) + if err != nil { + t.Error(err) + panic(err) + } + + err = testEngine.CreateTables(&Message{}, &MessageUser{}, &MessageType{}) + if err != nil { + t.Error(err) + panic(err) + } + + var sender = MessageUser{Name: "sender"} + var receiver = MessageUser{Name: "receiver"} + var msgtype = MessageType{Name: "type"} + _, err = testEngine.Insert(&sender, &receiver, &msgtype) + if err != nil { + t.Error(err) + panic(err) + } + + msg := Message{ + MessageBase: MessageBase{ + Id: msgtype.Id, + }, + Title: "test", + Content: "test", + Uid: sender.Id, + ToUid: receiver.Id, + } + + session := testEngine.NewSession() + defer session.Close() + + // MSSQL deny insert identity column excep declare as below + if testEngine.Dialect().DBType() == schemas.MSSQL { + err = session.Begin() + assert.NoError(t, err) + _, err = session.Exec("SET IDENTITY_INSERT message ON") + assert.NoError(t, err) + } + _, err = session.Insert(&msg) + assert.NoError(t, err) + + if testEngine.Dialect().DBType() == schemas.MSSQL { + err = session.Commit() + assert.NoError(t, err) + } + + var mapper = testEngine.GetTableMapper().Obj2Table + var quote = testEngine.Quote + userTableName := quote(testEngine.TableName(mapper("MessageUser"), true)) + typeTableName := quote(testEngine.TableName(mapper("MessageType"), true)) + msgTableName := quote(testEngine.TableName(mapper("Message"), true)) + + list := make([]MessageExtend3, 0) + err = session.Table(msgTableName).Join("LEFT", []string{userTableName, "sender"}, "`sender`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Uid")+"`"). + Join("LEFT", []string{userTableName, "receiver"}, "`receiver`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("ToUid")+"`"). + Join("LEFT", []string{typeTableName, "type"}, "`type`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Id")+"`"). + Find(&list) + assert.NoError(t, err) + + if len(list) != 1 { + err = errors.New(fmt.Sprintln("should have 1 message, got", len(list))) + t.Error(err) + panic(err) + } + + if list[0].Message.Id != msg.Id { + err = errors.New(fmt.Sprintln("should message equal", list[0].Message, msg)) + t.Error(err) + panic(err) + } + + if list[0].Sender.Id != sender.Id || list[0].Sender.Name != sender.Name { + err = errors.New(fmt.Sprintln("should sender equal", list[0].Sender, sender)) + t.Error(err) + panic(err) + } + + if list[0].Receiver.Id != receiver.Id || list[0].Receiver.Name != receiver.Name { + err = errors.New(fmt.Sprintln("should receiver equal", list[0].Receiver, receiver)) + t.Error(err) + panic(err) + } + + if list[0].Type.Id != msgtype.Id || list[0].Type.Name != msgtype.Name { + err = errors.New(fmt.Sprintln("should msgtype equal", list[0].Type, msgtype)) + t.Error(err) + panic(err) + } +} + +func TestExtends4(t *testing.T) { + assert.NoError(t, prepareEngine()) + + err := testEngine.DropTables(&Message{}, &MessageUser{}, &MessageType{}) + if err != nil { + t.Error(err) + panic(err) + } + + err = testEngine.CreateTables(&Message{}, &MessageUser{}, &MessageType{}) + if err != nil { + t.Error(err) + panic(err) + } + + var sender = MessageUser{Name: "sender"} + var msgtype = MessageType{Name: "type"} + _, err = testEngine.Insert(&sender, &msgtype) + if err != nil { + t.Error(err) + panic(err) + } + + msg := Message{ + MessageBase: MessageBase{ + Id: msgtype.Id, + }, + Title: "test", + Content: "test", + Uid: sender.Id, + } + + session := testEngine.NewSession() + defer session.Close() + + // MSSQL deny insert identity column excep declare as below + if testEngine.Dialect().DBType() == schemas.MSSQL { + err = session.Begin() + assert.NoError(t, err) + _, err = session.Exec("SET IDENTITY_INSERT message ON") + assert.NoError(t, err) + } + _, err = session.Insert(&msg) + assert.NoError(t, err) + + if testEngine.Dialect().DBType() == schemas.MSSQL { + err = session.Commit() + assert.NoError(t, err) + } + + var mapper = testEngine.GetTableMapper().Obj2Table + var quote = testEngine.Quote + userTableName := quote(testEngine.TableName(mapper("MessageUser"), true)) + typeTableName := quote(testEngine.TableName(mapper("MessageType"), true)) + msgTableName := quote(testEngine.TableName(mapper("Message"), true)) + + list := make([]MessageExtend4, 0) + err = session.Table(msgTableName).Join("LEFT", userTableName, userTableName+".`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Uid")+"`"). + Join("LEFT", typeTableName, typeTableName+".`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Id")+"`"). + Find(&list) + if err != nil { + t.Error(err) + panic(err) + } + + if len(list) != 1 { + err = errors.New(fmt.Sprintln("should have 1 message, got", len(list))) + t.Error(err) + panic(err) + } + + if list[0].Message.Id != msg.Id { + err = errors.New(fmt.Sprintln("should message equal", list[0].Message, msg)) + t.Error(err) + panic(err) + } + + if list[0].MessageUser.Id != sender.Id || list[0].MessageUser.Name != sender.Name { + err = errors.New(fmt.Sprintln("should sender equal", list[0].MessageUser, sender)) + t.Error(err) + panic(err) + } + + if list[0].MessageType.Id != msgtype.Id || list[0].MessageType.Name != msgtype.Name { + err = errors.New(fmt.Sprintln("should msgtype equal", list[0].MessageType, msgtype)) + t.Error(err) + panic(err) + } +} + +type Size struct { + ID int64 `xorm:"int(4) 'id' pk autoincr"` + Width float32 `json:"width" xorm:"float 'Width'"` + Height float32 `json:"height" xorm:"float 'Height'"` +} + +type Book struct { + ID int64 `xorm:"int(4) 'id' pk autoincr"` + SizeOpen *Size `xorm:"extends('Open')"` + SizeClosed *Size `xorm:"extends('Closed')"` + Size *Size `xorm:"extends('')"` +} + +func TestExtends5(t *testing.T) { + assert.NoError(t, prepareEngine()) + err := testEngine.DropTables(&Book{}, &Size{}) + if err != nil { + t.Error(err) + panic(err) + } + + err = testEngine.CreateTables(&Size{}, &Book{}) + if err != nil { + t.Error(err) + panic(err) + } + + var sc = Size{Width: 0.2, Height: 0.4} + var so = Size{Width: 0.2, Height: 0.8} + var s = Size{Width: 0.15, Height: 1.5} + var bk1 = Book{ + SizeOpen: &so, + SizeClosed: &sc, + Size: &s, + } + var bk2 = Book{ + SizeOpen: &so, + } + var bk3 = Book{ + SizeClosed: &sc, + Size: &s, + } + var bk4 = Book{} + var bk5 = Book{Size: &s} + _, err = testEngine.Insert(&sc, &so, &s, &bk1, &bk2, &bk3, &bk4, &bk5) + if err != nil { + t.Fatal(err) + } + + var books = map[int64]Book{ + bk1.ID: bk1, + bk2.ID: bk2, + bk3.ID: bk3, + bk4.ID: bk4, + bk5.ID: bk5, + } + + session := testEngine.NewSession() + defer session.Close() + + var mapper = testEngine.GetTableMapper().Obj2Table + var quote = testEngine.Quote + bookTableName := quote(testEngine.TableName(mapper("Book"), true)) + sizeTableName := quote(testEngine.TableName(mapper("Size"), true)) + + list := make([]Book, 0) + err = session. + Select(fmt.Sprintf( + "%s.%s, sc.%s AS %s, sc.%s AS %s, s.%s, s.%s", + quote(bookTableName), + quote("id"), + quote("Width"), + quote("ClosedWidth"), + quote("Height"), + quote("ClosedHeight"), + quote("Width"), + quote("Height"), + )). + Table(bookTableName). + Join( + "LEFT", + sizeTableName+" AS `sc`", + bookTableName+".`SizeClosed`=sc.`id`", + ). + Join( + "LEFT", + sizeTableName+" AS `s`", + bookTableName+".`Size`=s.`id`", + ). + Find(&list) + if err != nil { + t.Error(err) + panic(err) + } + + for _, book := range list { + if ok := assert.Equal(t, books[book.ID].SizeClosed.Width, book.SizeClosed.Width); !ok { + t.Error("Not bounded size closed") + panic("Not bounded size closed") + } + + if ok := assert.Equal(t, books[book.ID].SizeClosed.Height, book.SizeClosed.Height); !ok { + t.Error("Not bounded size closed") + panic("Not bounded size closed") + } + + if books[book.ID].Size != nil || book.Size != nil { + if ok := assert.Equal(t, books[book.ID].Size.Width, book.Size.Width); !ok { + t.Error("Not bounded size") + panic("Not bounded size") + } + + if ok := assert.Equal(t, books[book.ID].Size.Height, book.Size.Height); !ok { + t.Error("Not bounded size") + panic("Not bounded size") + } + } + } +} + +func TestCacheTag(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type CacheDomain struct { + Id int64 `xorm:"pk cache"` + Name string + } + + assert.NoError(t, testEngine.CreateTables(&CacheDomain{})) + assert.True(t, testEngine.GetCacher(testEngine.TableName(&CacheDomain{})) != nil) +} + +func TestNoCacheTag(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type NoCacheDomain struct { + Id int64 `xorm:"pk nocache"` + Name string + } + + assert.NoError(t, testEngine.CreateTables(&NoCacheDomain{})) + assert.True(t, testEngine.GetCacher(testEngine.TableName(&NoCacheDomain{})) == nil) +} + +type IDGonicMapper struct { + ID int64 +} + +func TestGonicMapperID(t *testing.T) { + assert.NoError(t, prepareEngine()) + + oldMapper := testEngine.GetColumnMapper() + testEngine.UnMapType(rValue(new(IDGonicMapper)).Type()) + testEngine.SetMapper(names.LintGonicMapper) + defer func() { + testEngine.UnMapType(rValue(new(IDGonicMapper)).Type()) + testEngine.SetMapper(oldMapper) + }() + + err := testEngine.CreateTables(new(IDGonicMapper)) + if err != nil { + t.Fatal(err) + } + + tables, err := testEngine.DBMetas() + if err != nil { + t.Fatal(err) + } + + for _, tb := range tables { + if tb.Name == "id_gonic_mapper" { + if len(tb.PKColumns()) != 1 || tb.PKColumns()[0].Name != "id" { + t.Fatal(tb) + } + return + } + } + + t.Fatal("not table id_gonic_mapper") +} + +type IDSameMapper struct { + ID int64 +} + +func TestSameMapperID(t *testing.T) { + assert.NoError(t, prepareEngine()) + + oldMapper := testEngine.GetColumnMapper() + testEngine.UnMapType(rValue(new(IDSameMapper)).Type()) + testEngine.SetMapper(names.SameMapper{}) + defer func() { + testEngine.UnMapType(rValue(new(IDSameMapper)).Type()) + testEngine.SetMapper(oldMapper) + }() + + err := testEngine.CreateTables(new(IDSameMapper)) + if err != nil { + t.Fatal(err) + } + + tables, err := testEngine.DBMetas() + if err != nil { + t.Fatal(err) + } + + for _, tb := range tables { + if tb.Name == "IDSameMapper" { + if len(tb.PKColumns()) != 1 || tb.PKColumns()[0].Name != "ID" { + t.Fatalf("tb %s tb.PKColumns() is %d not 1, tb.PKColumns()[0].Name is %s not ID", tb.Name, len(tb.PKColumns()), tb.PKColumns()[0].Name) + } + return + } + } + t.Fatal("not table IDSameMapper") +} + +type UserCU struct { + Id int64 + Name string + Created time.Time `xorm:"created"` + Updated time.Time `xorm:"updated"` +} + +func TestCreatedAndUpdated(t *testing.T) { + assert.NoError(t, prepareEngine()) + + u := new(UserCU) + err := testEngine.DropTables(u) + assert.NoError(t, err) + + err = testEngine.CreateTables(u) + assert.NoError(t, err) + + u.Name = "sss" + cnt, err := testEngine.Insert(u) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + u.Name = "xxx" + cnt, err = testEngine.ID(u.Id).Update(u) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + u.Id = 0 + u.Created = time.Now().Add(-time.Hour * 24 * 365) + u.Updated = u.Created + cnt, err = testEngine.NoAutoTime().Insert(u) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) +} + +type StrangeName struct { + Id_t int64 `xorm:"pk autoincr"` + Name string +} + +func TestStrangeName(t *testing.T) { + assert.NoError(t, prepareEngine()) + + err := testEngine.DropTables(new(StrangeName)) + assert.NoError(t, err) + + err = testEngine.CreateTables(new(StrangeName)) + assert.NoError(t, err) + + _, err = testEngine.Insert(&StrangeName{Name: "sfsfdsfds"}) + assert.NoError(t, err) + + beans := make([]StrangeName, 0) + err = testEngine.Find(&beans) + assert.NoError(t, err) +} + +func TestCreatedUpdated(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type CreatedUpdated struct { + Id int64 + Name string + Value float64 `xorm:"numeric"` + Created time.Time `xorm:"created"` + Created2 time.Time `xorm:"created"` + Updated time.Time `xorm:"updated"` + } + + err := testEngine.Sync2(&CreatedUpdated{}) + assert.NoError(t, err) + + c := &CreatedUpdated{Name: "test"} + _, err = testEngine.Insert(c) + assert.NoError(t, err) + + c2 := new(CreatedUpdated) + has, err := testEngine.ID(c.Id).Get(c2) + assert.NoError(t, err) + + assert.True(t, has) + + c2.Value -= 1 + _, err = testEngine.ID(c2.Id).Update(c2) + assert.NoError(t, err) +} + +func TestCreatedUpdatedInt64(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type CreatedUpdatedInt64 struct { + Id int64 + Name string + Value float64 `xorm:"numeric"` + Created int64 `xorm:"created"` + Created2 int64 `xorm:"created"` + Updated int64 `xorm:"updated"` + } + + assertSync(t, &CreatedUpdatedInt64{}) + + c := &CreatedUpdatedInt64{Name: "test"} + _, err := testEngine.Insert(c) + assert.NoError(t, err) + + c2 := new(CreatedUpdatedInt64) + has, err := testEngine.ID(c.Id).Get(c2) + assert.NoError(t, err) + assert.True(t, has) + + c2.Value -= 1 + _, err = testEngine.ID(c2.Id).Update(c2) + assert.NoError(t, err) +} + +type Lowercase struct { + Id int64 + Name string + ended int64 `xorm:"-"` +} + +func TestLowerCase(t *testing.T) { + assert.NoError(t, prepareEngine()) + + err := testEngine.Sync2(&Lowercase{}) + assert.NoError(t, err) + _, err = testEngine.Where("id > 0").Delete(&Lowercase{}) + assert.NoError(t, err) + + _, err = testEngine.Insert(&Lowercase{ended: 1}) + assert.NoError(t, err) + + ls := make([]Lowercase, 0) + err = testEngine.Find(&ls) + assert.NoError(t, err) + assert.EqualValues(t, 1, len(ls)) +} + +func TestAutoIncrTag(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type TestAutoIncr1 struct { + Id int64 + } + + tb := testEngine.TableInfo(new(TestAutoIncr1)) + cols := tb.Columns() + assert.EqualValues(t, 1, len(cols)) + assert.True(t, cols[0].IsAutoIncrement) + assert.True(t, cols[0].IsPrimaryKey) + assert.Equal(t, "id", cols[0].Name) + + type TestAutoIncr2 struct { + Id int64 `xorm:"id"` + } + + tb = testEngine.TableInfo(new(TestAutoIncr2)) + cols = tb.Columns() + assert.EqualValues(t, 1, len(cols)) + assert.False(t, cols[0].IsAutoIncrement) + assert.False(t, cols[0].IsPrimaryKey) + assert.Equal(t, "id", cols[0].Name) + + type TestAutoIncr3 struct { + Id int64 `xorm:"'ID'"` + } + + tb = testEngine.TableInfo(new(TestAutoIncr3)) + cols = tb.Columns() + assert.EqualValues(t, 1, len(cols)) + assert.False(t, cols[0].IsAutoIncrement) + assert.False(t, cols[0].IsPrimaryKey) + assert.Equal(t, "ID", cols[0].Name) + + type TestAutoIncr4 struct { + Id int64 `xorm:"pk"` + } + + tb = testEngine.TableInfo(new(TestAutoIncr4)) + cols = tb.Columns() + assert.EqualValues(t, 1, len(cols)) + assert.False(t, cols[0].IsAutoIncrement) + assert.True(t, cols[0].IsPrimaryKey) + assert.Equal(t, "id", cols[0].Name) +} + +func TestTagComment(t *testing.T) { + assert.NoError(t, prepareEngine()) + // FIXME: only support mysql + if testEngine.Dialect().DriverName() != schemas.MYSQL { + return + } + + type TestComment1 struct { + Id int64 `xorm:"comment(主键)"` + } + + assert.NoError(t, testEngine.Sync2(new(TestComment1))) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + assert.EqualValues(t, 1, len(tables)) + assert.EqualValues(t, 1, len(tables[0].Columns())) + assert.EqualValues(t, "主键", tables[0].Columns()[0].Comment) + + assert.NoError(t, testEngine.DropTables(new(TestComment1))) + + type TestComment2 struct { + Id int64 `xorm:"comment('主键')"` + } + + assert.NoError(t, testEngine.Sync2(new(TestComment2))) + + tables, err = testEngine.DBMetas() + assert.NoError(t, err) + assert.EqualValues(t, 1, len(tables)) + assert.EqualValues(t, 1, len(tables[0].Columns())) + assert.EqualValues(t, "主键", tables[0].Columns()[0].Comment) +} + +func TestTagDefault(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type DefaultStruct struct { + Id int64 + Name string + Age int `xorm:"default(10)"` + } + + assertSync(t, new(DefaultStruct)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + + var defaultVal string + var isDefaultExist bool + tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct") + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn("age") + assert.NotNil(t, col) + defaultVal = col.Default + isDefaultExist = !col.DefaultIsEmpty + break + } + } + assert.True(t, isDefaultExist) + assert.EqualValues(t, "10", defaultVal) + + cnt, err := testEngine.Omit("age").Insert(&DefaultStruct{ + Name: "test", + Age: 20, + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var s DefaultStruct + has, err := testEngine.ID(1).Get(&s) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, 10, s.Age) + assert.EqualValues(t, "test", s.Name) +} + +func TestTagDefault2(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type DefaultStruct2 struct { + Id int64 + Name string + } + + assertSync(t, new(DefaultStruct2)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + + var defaultVal string + var isDefaultExist bool + tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct2") + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn("name") + assert.NotNil(t, col) + defaultVal = col.Default + isDefaultExist = !col.DefaultIsEmpty + break + } + } + assert.False(t, isDefaultExist, fmt.Sprintf("default value is --%v--", defaultVal)) + assert.EqualValues(t, "", defaultVal) +} + +func TestTagDefault3(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type DefaultStruct3 struct { + Id int64 + Name string `xorm:"default('myname')"` + } + + assertSync(t, new(DefaultStruct3)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + + var defaultVal string + var isDefaultExist bool + tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct3") + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn("name") + assert.NotNil(t, col) + defaultVal = col.Default + isDefaultExist = !col.DefaultIsEmpty + break + } + } + assert.True(t, isDefaultExist) + assert.EqualValues(t, "'myname'", defaultVal) +} + +func TestTagDefault4(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type DefaultStruct4 struct { + Id int64 + Created time.Time `xorm:"default(CURRENT_TIMESTAMP)"` + } + + assertSync(t, new(DefaultStruct4)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + + var defaultVal string + var isDefaultExist bool + tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct4") + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn("created") + assert.NotNil(t, col) + defaultVal = col.Default + isDefaultExist = !col.DefaultIsEmpty + break + } + } + assert.True(t, isDefaultExist) + assert.True(t, "CURRENT_TIMESTAMP" == defaultVal || + "now()" == defaultVal || + "getdate" == defaultVal, defaultVal) +} + +func TestTagDefault5(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type DefaultStruct5 struct { + Id int64 + Created time.Time `xorm:"default('2006-01-02 15:04:05')"` + } + + assertSync(t, new(DefaultStruct5)) + table := testEngine.TableInfo(new(DefaultStruct5)) + createdCol := table.GetColumn("created") + assert.NotNil(t, createdCol) + assert.EqualValues(t, "'2006-01-02 15:04:05'", createdCol.Default) + assert.False(t, createdCol.DefaultIsEmpty) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + + var defaultVal string + var isDefaultExist bool + tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct5") + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn("created") + assert.NotNil(t, col) + defaultVal = col.Default + isDefaultExist = !col.DefaultIsEmpty + break + } + } + assert.True(t, isDefaultExist) + assert.EqualValues(t, "'2006-01-02 15:04:05'", defaultVal) +} + +func TestTagDefault6(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type DefaultStruct6 struct { + Id int64 + IsMan bool `xorm:"default(true)"` + } + + assertSync(t, new(DefaultStruct6)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + + var defaultVal string + var isDefaultExist bool + tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct6") + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn("is_man") + assert.NotNil(t, col) + defaultVal = col.Default + isDefaultExist = !col.DefaultIsEmpty + break + } + } + assert.True(t, isDefaultExist) + if defaultVal == "1" { + defaultVal = "true" + } else if defaultVal == "0" { + defaultVal = "false" + } + assert.EqualValues(t, "true", defaultVal) +} + +func TestTagsDirection(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type OnlyFromDBStruct struct { + Id int64 + Name string + Uuid string `xorm:"<- default '1'"` + } + + assertSync(t, new(OnlyFromDBStruct)) + + cnt, err := testEngine.Insert(&OnlyFromDBStruct{ + Name: "test", + Uuid: "2", + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var s OnlyFromDBStruct + has, err := testEngine.ID(1).Get(&s) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "1", s.Uuid) + assert.EqualValues(t, "test", s.Name) + + cnt, err = testEngine.ID(1).Update(&OnlyFromDBStruct{ + Uuid: "3", + Name: "test1", + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var s3 OnlyFromDBStruct + has, err = testEngine.ID(1).Get(&s3) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "1", s3.Uuid) + assert.EqualValues(t, "test1", s3.Name) + + type OnlyToDBStruct struct { + Id int64 + Name string + Uuid string `xorm:"->"` + } + + assertSync(t, new(OnlyToDBStruct)) + + cnt, err = testEngine.Insert(&OnlyToDBStruct{ + Name: "test", + Uuid: "2", + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var s2 OnlyToDBStruct + has, err = testEngine.ID(1).Get(&s2) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "", s2.Uuid) + assert.EqualValues(t, "test", s2.Name) +} + +func TestTagTime(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type TagUTCStruct struct { + Id int64 + Name string + Created time.Time `xorm:"created utc"` + } + + assertSync(t, new(TagUTCStruct)) + + assert.EqualValues(t, time.Local.String(), testEngine.GetTZLocation().String()) + + s := TagUTCStruct{ + Name: "utc", + } + cnt, err := testEngine.Insert(&s) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var u TagUTCStruct + has, err := testEngine.ID(1).Get(&u) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, s.Created.Format("2006-01-02 15:04:05"), u.Created.Format("2006-01-02 15:04:05")) + + var tm string + has, err = testEngine.Table("tag_u_t_c_struct").Cols("created").Get(&tm) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, s.Created.UTC().Format("2006-01-02 15:04:05"), + strings.Replace(strings.Replace(tm, "T", " ", -1), "Z", "", -1)) +} + +func TestTagAutoIncr(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type TagAutoIncr struct { + Id int64 + Name string + } + + assertSync(t, new(TagAutoIncr)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + assert.EqualValues(t, 1, len(tables)) + assert.EqualValues(t, tableMapper.Obj2Table("TagAutoIncr"), tables[0].Name) + col := tables[0].GetColumn(colMapper.Obj2Table("Id")) + assert.NotNil(t, col) + assert.True(t, col.IsPrimaryKey) + assert.True(t, col.IsAutoIncrement) + + col2 := tables[0].GetColumn(colMapper.Obj2Table("Name")) + assert.NotNil(t, col2) + assert.False(t, col2.IsPrimaryKey) + assert.False(t, col2.IsAutoIncrement) +} + +func TestTagPrimarykey(t *testing.T) { + assert.NoError(t, prepareEngine()) + type TagPrimaryKey struct { + Id int64 `xorm:"pk"` + Name string `xorm:"VARCHAR(20) pk"` + } + + assertSync(t, new(TagPrimaryKey)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + assert.EqualValues(t, 1, len(tables)) + assert.EqualValues(t, tableMapper.Obj2Table("TagPrimaryKey"), tables[0].Name) + col := tables[0].GetColumn(colMapper.Obj2Table("Id")) + assert.NotNil(t, col) + assert.True(t, col.IsPrimaryKey) + assert.False(t, col.IsAutoIncrement) + + col2 := tables[0].GetColumn(colMapper.Obj2Table("Name")) + assert.NotNil(t, col2) + assert.True(t, col2.IsPrimaryKey) + assert.False(t, col2.IsAutoIncrement) +} + +type VersionS struct { + Id int64 + Name string + Ver int `xorm:"version"` + Created time.Time `xorm:"created"` +} + +func TestVersion1(t *testing.T) { + assert.NoError(t, prepareEngine()) + + err := testEngine.DropTables(new(VersionS)) + if err != nil { + t.Error(err) + panic(err) + } + + err = testEngine.CreateTables(new(VersionS)) + if err != nil { + t.Error(err) + panic(err) + } + + ver := &VersionS{Name: "sfsfdsfds"} + _, err = testEngine.Insert(ver) + if err != nil { + t.Error(err) + panic(err) + } + fmt.Println(ver) + if ver.Ver != 1 { + err = errors.New("insert error") + t.Error(err) + panic(err) + } + + newVer := new(VersionS) + has, err := testEngine.ID(ver.Id).Get(newVer) + if err != nil { + t.Error(err) + panic(err) + } + + if !has { + t.Error(fmt.Errorf("no version id is %v", ver.Id)) + panic(err) + } + fmt.Println(newVer) + if newVer.Ver != 1 { + err = errors.New("insert error") + t.Error(err) + panic(err) + } + + newVer.Name = "-------" + _, err = testEngine.ID(ver.Id).Update(newVer) + if err != nil { + t.Error(err) + panic(err) + } + if newVer.Ver != 2 { + err = errors.New("update should set version back to struct") + t.Error(err) + } + + newVer = new(VersionS) + has, err = testEngine.ID(ver.Id).Get(newVer) + if err != nil { + t.Error(err) + panic(err) + } + fmt.Println(newVer) + if newVer.Ver != 2 { + err = errors.New("update error") + t.Error(err) + panic(err) + } +} + +func TestVersion2(t *testing.T) { + assert.NoError(t, prepareEngine()) + + err := testEngine.DropTables(new(VersionS)) + if err != nil { + t.Error(err) + panic(err) + } + + err = testEngine.CreateTables(new(VersionS)) + if err != nil { + t.Error(err) + panic(err) + } + + var vers = []VersionS{ + {Name: "sfsfdsfds"}, + {Name: "xxxxx"}, + } + _, err = testEngine.Insert(vers) + if err != nil { + t.Error(err) + panic(err) + } + + fmt.Println(vers) + + for _, v := range vers { + if v.Ver != 1 { + err := errors.New("version should be 1") + t.Error(err) + panic(err) + } + } +} + +type VersionUintS struct { + Id int64 + Name string + Ver uint `xorm:"version"` + Created time.Time `xorm:"created"` +} + +func TestVersion3(t *testing.T) { + assert.NoError(t, prepareEngine()) + + err := testEngine.DropTables(new(VersionUintS)) + if err != nil { + t.Error(err) + panic(err) + } + + err = testEngine.CreateTables(new(VersionUintS)) + if err != nil { + t.Error(err) + panic(err) + } + + ver := &VersionUintS{Name: "sfsfdsfds"} + _, err = testEngine.Insert(ver) + if err != nil { + t.Error(err) + panic(err) + } + fmt.Println(ver) + if ver.Ver != 1 { + err = errors.New("insert error") + t.Error(err) + panic(err) + } + + newVer := new(VersionUintS) + has, err := testEngine.ID(ver.Id).Get(newVer) + if err != nil { + t.Error(err) + panic(err) + } + + if !has { + t.Error(fmt.Errorf("no version id is %v", ver.Id)) + panic(err) + } + fmt.Println(newVer) + if newVer.Ver != 1 { + err = errors.New("insert error") + t.Error(err) + panic(err) + } + + newVer.Name = "-------" + _, err = testEngine.ID(ver.Id).Update(newVer) + if err != nil { + t.Error(err) + panic(err) + } + if newVer.Ver != 2 { + err = errors.New("update should set version back to struct") + t.Error(err) + } + + newVer = new(VersionUintS) + has, err = testEngine.ID(ver.Id).Get(newVer) + if err != nil { + t.Error(err) + panic(err) + } + fmt.Println(newVer) + if newVer.Ver != 2 { + err = errors.New("update error") + t.Error(err) + panic(err) + } +} + +func TestVersion4(t *testing.T) { + assert.NoError(t, prepareEngine()) + + err := testEngine.DropTables(new(VersionUintS)) + if err != nil { + t.Error(err) + panic(err) + } + + err = testEngine.CreateTables(new(VersionUintS)) + if err != nil { + t.Error(err) + panic(err) + } + + var vers = []VersionUintS{ + {Name: "sfsfdsfds"}, + {Name: "xxxxx"}, + } + _, err = testEngine.Insert(vers) + if err != nil { + t.Error(err) + panic(err) + } + + fmt.Println(vers) + + for _, v := range vers { + if v.Ver != 1 { + err := errors.New("version should be 1") + t.Error(err) + panic(err) + } + } +} diff --git a/types_test.go b/types_test.go index 1e21907c..53872372 100644 --- a/types_test.go +++ b/types_test.go @@ -9,8 +9,10 @@ import ( "fmt" "testing" - "github.com/stretchr/testify/assert" + "xorm.io/xorm/convert" "xorm.io/xorm/schemas" + + "github.com/stretchr/testify/assert" ) func TestArrayField(t *testing.T) { @@ -137,8 +139,8 @@ type ConvStruct struct { Conv ConvString Conv2 *ConvString Cfg1 ConvConfig - Cfg2 *ConvConfig `xorm:"TEXT"` - Cfg3 Conversion `xorm:"BLOB"` + Cfg2 *ConvConfig `xorm:"TEXT"` + Cfg3 convert.Conversion `xorm:"BLOB"` Slice SliceType } @@ -267,11 +269,11 @@ type Status struct { } var ( - _ Conversion = &Status{} - Registered Status = Status{"Registered", "white"} - Approved Status = Status{"Approved", "green"} - Removed Status = Status{"Removed", "red"} - Statuses map[string]Status = map[string]Status{ + _ convert.Conversion = &Status{} + Registered Status = Status{"Registered", "white"} + Approved Status = Status{"Approved", "green"} + Removed Status = Status{"Removed", "red"} + Statuses map[string]Status = map[string]Status{ Registered.Name: Registered, Approved.Name: Approved, Removed.Name: Removed, diff --git a/xorm.go b/xorm.go index 0f27f8e4..f3230aa1 100644 --- a/xorm.go +++ b/xorm.go @@ -21,6 +21,7 @@ import ( "xorm.io/xorm/log" "xorm.io/xorm/names" "xorm.io/xorm/schemas" + "xorm.io/xorm/tags" ) const ( @@ -65,9 +66,7 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { dialect: dialect, Tables: make(map[reflect.Type]*schemas.Table), mutex: &sync.RWMutex{}, - TagIdentifier: "xorm", TZLocation: time.Local, - tagHandlers: defaultTagHandlers, defaultContext: context.Background(), cacherMgr: caches.NewManager(), } @@ -81,7 +80,8 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { logger := log.NewSimpleLogger(os.Stdout) logger.SetLevel(log.LOG_INFO) engine.SetLogger(logger) - engine.SetMapper(names.NewCacheMapper(new(names.SnakeMapper))) + mapper := names.NewCacheMapper(new(names.SnakeMapper)) + engine.tagParser = tags.NewParser("xorm", dialect, mapper, mapper, engine.cacherMgr) runtime.SetFinalizer(engine, close)