Browse Source

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: #1547
tags/v1.0.0
Lunny Xiao 1 month ago
parent
commit
e2f9100419
28 changed files with 1890 additions and 1882 deletions
  1. +2
    -2
      .drone.yml
  2. +0
    -7
      convert.go
  3. +12
    -0
      convert/conversion.go
  4. +10
    -221
      engine.go
  5. +2
    -1
      engine_cond.go
  6. +4
    -4
      engine_group.go
  7. +7
    -7
      engine_table.go
  8. +0
    -15
      helpers.go
  9. +22
    -0
      internal/utils/slice.go
  10. +4
    -3
      session.go
  11. +5
    -4
      session_convert.go
  12. +1
    -1
      session_delete.go
  13. +1
    -1
      session_find.go
  14. +1
    -1
      session_get.go
  15. +1
    -1
      session_insert.go
  16. +3
    -2
      session_schema.go
  17. +3
    -2
      statement.go
  18. +0
    -35
      tag_cache_test.go
  19. +0
    -608
      tag_extends_test.go
  20. +0
    -85
      tag_id_test.go
  21. +0
    -600
      tag_test.go
  22. +0
    -242
      tag_version_test.go
  23. +239
    -0
      tags/parser.go
  24. +29
    -29
      tags/tag.go
  25. +30
    -0
      tags/tag_test.go
  26. +1501
    -0
      tags_test.go
  27. +10
    -8
      types_test.go
  28. +3
    -3
      xorm.go

+ 2
- 2
.drone.yml View File

@@ -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


+ 0
- 7
convert.go View File

@@ -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)
}

+ 12
- 0
convert/conversion.go View File

@@ -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)
}

+ 10
- 221
engine.go View File

@@ -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


+ 2
- 1
engine_cond.go View File

@@ -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()


+ 4
- 4
engine_group.go View File

@@ -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)
}
}



+ 7
- 7
engine_table.go View File

@@ -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))
}


+ 0
- 15
helpers.go View File

@@ -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)
}


+ 22
- 0
internal/utils/slice.go View File

@@ -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
}

+ 4
- 3
session.go View File

@@ -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
}


+ 5
- 4
session_convert.go View File

@@ -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


+ 1
- 1
session_delete.go View File

@@ -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 {


+ 1
- 1
session_find.go View File

@@ -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
}


+ 1
- 1
session_get.go View File

@@ -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


+ 1
- 1
session_insert.go View File

@@ -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
}


+ 3
- 2
session_schema.go View File

@@ -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
}


+ 3
- 2
statement.go View File

@@ -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)


+ 0
- 35
tag_cache_test.go View File

@@ -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)
}

+ 0
- 608
tag_extends_test.go View File

@@ -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")
}
}
}
}

+ 0
- 85
tag_id_test.go View File

@@ -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")
}

+ 0
- 600
tag_test.go View File

@@ -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)
}

+ 0
- 242
tag_version_test.go View File

@@ -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)
}