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
This commit is contained in:
parent
aa522f7d98
commit
e2f9100419
|
@ -22,8 +22,8 @@ steps:
|
||||||
commands:
|
commands:
|
||||||
- make test-sqlite
|
- make test-sqlite
|
||||||
- TEST_CACHE_ENABLE=true make test-sqlite
|
- TEST_CACHE_ENABLE=true make test-sqlite
|
||||||
- go test ./caches/... ./core/... ./dialects/... ./internal/... \
|
- go test ./caches/... ./convert/... ./core/... ./dialects/... \
|
||||||
./log/... ./migrate/... ./names/... ./schemas/...
|
./log/... ./migrate/... ./names/... ./schemas/... ./tags/...
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
- push
|
- push
|
||||||
|
|
|
@ -346,10 +346,3 @@ func asBool(bs []byte) (bool, error) {
|
||||||
}
|
}
|
||||||
return strconv.ParseBool(string(bs))
|
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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
229
engine.go
229
engine.go
|
@ -28,6 +28,7 @@ import (
|
||||||
"xorm.io/xorm/log"
|
"xorm.io/xorm/log"
|
||||||
"xorm.io/xorm/names"
|
"xorm.io/xorm/names"
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
|
"xorm.io/xorm/tags"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Engine is the major struct of xorm, it means a database manager.
|
// Engine is the major struct of xorm, it means a database manager.
|
||||||
|
@ -36,9 +37,6 @@ type Engine struct {
|
||||||
db *core.DB
|
db *core.DB
|
||||||
dialect dialects.Dialect
|
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
|
mutex *sync.RWMutex
|
||||||
|
@ -50,12 +48,12 @@ type Engine struct {
|
||||||
TZLocation *time.Location // The timezone of the application
|
TZLocation *time.Location // The timezone of the application
|
||||||
DatabaseTZ *time.Location // The timezone of the database
|
DatabaseTZ *time.Location // The timezone of the database
|
||||||
|
|
||||||
tagHandlers map[string]tagHandler
|
|
||||||
|
|
||||||
engineGroup *EngineGroup
|
engineGroup *EngineGroup
|
||||||
cacherMgr *caches.Manager
|
|
||||||
|
|
||||||
defaultContext context.Context
|
defaultContext context.Context
|
||||||
|
|
||||||
|
tagParser *tags.Parser
|
||||||
|
cacherMgr *caches.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func (engine *Engine) SetCacher(tableName string, cacher caches.Cacher) {
|
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
|
// SetTableMapper set the table name mapping rule
|
||||||
func (engine *Engine) SetTableMapper(mapper names.Mapper) {
|
func (engine *Engine) SetTableMapper(mapper names.Mapper) {
|
||||||
engine.TableMapper = mapper
|
engine.tagParser.TableMapper = mapper
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetColumnMapper set the column name mapping rule
|
// SetColumnMapper set the column name mapping rule
|
||||||
func (engine *Engine) SetColumnMapper(mapper names.Mapper) {
|
func (engine *Engine) SetColumnMapper(mapper names.Mapper) {
|
||||||
engine.ColumnMapper = mapper
|
engine.tagParser.ColumnMapper = mapper
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportInsertMany If engine's database support batch insert records like
|
// 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]
|
table, ok := engine.Tables[t]
|
||||||
if !ok {
|
if !ok {
|
||||||
var err error
|
var err error
|
||||||
table, err = engine.mapType(v)
|
table, err = engine.tagParser.MapType(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -813,215 +811,6 @@ func (engine *Engine) TableInfo(bean interface{}) *Table {
|
||||||
return &Table{tb, engine.TableName(bean)}
|
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
|
// IsTableEmpty if a table has any reocrd
|
||||||
func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
|
func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
|
||||||
session := engine.NewSession()
|
session := engine.NewSession()
|
||||||
|
@ -1542,12 +1331,12 @@ func (engine *Engine) formatTime(sqlTypeName string, t time.Time) (v interface{}
|
||||||
|
|
||||||
// GetColumnMapper returns the column name mapper
|
// GetColumnMapper returns the column name mapper
|
||||||
func (engine *Engine) GetColumnMapper() names.Mapper {
|
func (engine *Engine) GetColumnMapper() names.Mapper {
|
||||||
return engine.ColumnMapper
|
return engine.tagParser.ColumnMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTableMapper returns the table name mapper
|
// GetTableMapper returns the table name mapper
|
||||||
func (engine *Engine) GetTableMapper() names.Mapper {
|
func (engine *Engine) GetTableMapper() names.Mapper {
|
||||||
return engine.TableMapper
|
return engine.tagParser.TableMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTZLocation returns time zone of the application
|
// GetTZLocation returns time zone of the application
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
|
"xorm.io/xorm/convert"
|
||||||
"xorm.io/xorm/internal/utils"
|
"xorm.io/xorm/internal/utils"
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
)
|
)
|
||||||
|
@ -137,7 +138,7 @@ func (engine *Engine) buildConds(table *schemas.Table, bean interface{},
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val = engine.formatColTime(col, t)
|
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
|
continue
|
||||||
} else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok {
|
} else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok {
|
||||||
val, _ = valNul.Value()
|
val, _ = valNul.Value()
|
||||||
|
|
|
@ -112,9 +112,9 @@ func (eg *EngineGroup) Ping() error {
|
||||||
|
|
||||||
// SetColumnMapper set the column name mapping rule
|
// SetColumnMapper set the column name mapping rule
|
||||||
func (eg *EngineGroup) SetColumnMapper(mapper names.Mapper) {
|
func (eg *EngineGroup) SetColumnMapper(mapper names.Mapper) {
|
||||||
eg.Engine.ColumnMapper = mapper
|
eg.Engine.SetColumnMapper(mapper)
|
||||||
for i := 0; i < len(eg.slaves); i++ {
|
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
|
// SetTableMapper set the table name mapping rule
|
||||||
func (eg *EngineGroup) SetTableMapper(mapper names.Mapper) {
|
func (eg *EngineGroup) SetTableMapper(mapper names.Mapper) {
|
||||||
eg.Engine.TableMapper = mapper
|
eg.Engine.SetTableMapper(mapper)
|
||||||
for i := 0; i < len(eg.slaves); i++ {
|
for i := 0; i < len(eg.slaves); i++ {
|
||||||
eg.slaves[i].TableMapper = mapper
|
eg.slaves[i].SetTableMapper(mapper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,13 +72,13 @@ func (engine *Engine) tbNameNoSchema(tablename interface{}) string {
|
||||||
switch f.(type) {
|
switch f.(type) {
|
||||||
case string:
|
case string:
|
||||||
table = f.(string)
|
table = f.(string)
|
||||||
case TableName:
|
case names.TableName:
|
||||||
table = f.(TableName).TableName()
|
table = f.(names.TableName).TableName()
|
||||||
default:
|
default:
|
||||||
v := rValue(f)
|
v := rValue(f)
|
||||||
t := v.Type()
|
t := v.Type()
|
||||||
if t.Kind() == reflect.Struct {
|
if t.Kind() == reflect.Struct {
|
||||||
table = names.GetTableName(engine.TableMapper, v)
|
table = names.GetTableName(engine.GetTableMapper(), v)
|
||||||
} else {
|
} else {
|
||||||
table = engine.Quote(fmt.Sprintf("%v", f))
|
table = engine.Quote(fmt.Sprintf("%v", f))
|
||||||
}
|
}
|
||||||
|
@ -90,18 +90,18 @@ func (engine *Engine) tbNameNoSchema(tablename interface{}) string {
|
||||||
} else if l == 1 {
|
} else if l == 1 {
|
||||||
return engine.Quote(table)
|
return engine.Quote(table)
|
||||||
}
|
}
|
||||||
case TableName:
|
case names.TableName:
|
||||||
return tablename.(TableName).TableName()
|
return tablename.(names.TableName).TableName()
|
||||||
case string:
|
case string:
|
||||||
return tablename.(string)
|
return tablename.(string)
|
||||||
case reflect.Value:
|
case reflect.Value:
|
||||||
v := tablename.(reflect.Value)
|
v := tablename.(reflect.Value)
|
||||||
return names.GetTableName(engine.TableMapper, v)
|
return names.GetTableName(engine.GetTableMapper(), v)
|
||||||
default:
|
default:
|
||||||
v := rValue(tablename)
|
v := rValue(tablename)
|
||||||
t := v.Type()
|
t := v.Type()
|
||||||
if t.Kind() == reflect.Struct {
|
if t.Kind() == reflect.Struct {
|
||||||
return names.GetTableName(engine.TableMapper, v)
|
return names.GetTableName(engine.GetTableMapper(), v)
|
||||||
}
|
}
|
||||||
return engine.Quote(fmt.Sprintf("%v", tablename))
|
return engine.Quote(fmt.Sprintf("%v", tablename))
|
||||||
}
|
}
|
||||||
|
|
15
helpers.go
15
helpers.go
|
@ -8,7 +8,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -184,20 +183,6 @@ func structName(v reflect.Type) string {
|
||||||
return v.Name()
|
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 {
|
func indexName(tableName, idxName string) string {
|
||||||
return fmt.Sprintf("IDX_%v_%v", tableName, idxName)
|
return fmt.Sprintf("IDX_%v_%v", tableName, idxName)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"xorm.io/xorm/convert"
|
||||||
"xorm.io/xorm/core"
|
"xorm.io/xorm/core"
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
)
|
)
|
||||||
|
@ -453,7 +454,7 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b
|
||||||
}
|
}
|
||||||
|
|
||||||
if fieldValue.CanAddr() {
|
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 data, err := value2Bytes(&rawValue); err == nil {
|
||||||
if err := structConvert.FromDB(data); err != nil {
|
if err := structConvert.FromDB(data); err != nil {
|
||||||
return nil, err
|
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 data, err := value2Bytes(&rawValue); err == nil {
|
||||||
if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
|
if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
|
||||||
fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
|
fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
|
||||||
}
|
}
|
||||||
fieldValue.Interface().(Conversion).FromDB(data)
|
fieldValue.Interface().(convert.Conversion).FromDB(data)
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"xorm.io/xorm/convert"
|
||||||
"xorm.io/xorm/internal/utils"
|
"xorm.io/xorm/internal/utils"
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
)
|
)
|
||||||
|
@ -91,11 +92,11 @@ var (
|
||||||
|
|
||||||
// convert a db data([]byte) to a field value
|
// convert a db data([]byte) to a field value
|
||||||
func (session *Session) bytes2Value(col *schemas.Column, fieldValue *reflect.Value, data []byte) error {
|
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)
|
return structConvert.FromDB(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if structConvert, ok := fieldValue.Interface().(Conversion); ok {
|
if structConvert, ok := fieldValue.Interface().(convert.Conversion); ok {
|
||||||
return structConvert.FromDB(data)
|
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
|
// 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) {
|
func (session *Session) value2Interface(col *schemas.Column, fieldValue reflect.Value) (interface{}, error) {
|
||||||
if fieldValue.CanAddr() {
|
if fieldValue.CanAddr() {
|
||||||
if fieldConvert, ok := fieldValue.Addr().Interface().(Conversion); ok {
|
if fieldConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok {
|
||||||
data, err := fieldConvert.ToDB()
|
data, err := fieldConvert.ToDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
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()
|
data, err := fieldConvert.ToDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
@ -28,7 +28,7 @@ func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr stri
|
||||||
return ErrCacheFailed
|
return ErrCacheFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
cacher := session.engine.GetCacher(tableName)
|
cacher := session.engine.cacherMgr.GetCacher(tableName)
|
||||||
pkColumns := table.PKColumns()
|
pkColumns := table.PKColumns()
|
||||||
ids, err := caches.GetCacheSql(cacher, tableName, newsql, args)
|
ids, err := caches.GetCacheSql(cacher, tableName, newsql, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -329,7 +329,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
|
||||||
}
|
}
|
||||||
|
|
||||||
tableName := session.statement.TableName()
|
tableName := session.statement.TableName()
|
||||||
cacher := session.engine.GetCacher(tableName)
|
cacher := session.engine.cacherMgr.GetCacher(tableName)
|
||||||
if cacher == nil {
|
if cacher == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -280,7 +280,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf
|
||||||
}
|
}
|
||||||
|
|
||||||
tableName := session.statement.TableName()
|
tableName := session.statement.TableName()
|
||||||
cacher := session.engine.GetCacher(tableName)
|
cacher := session.engine.cacherMgr.GetCacher(tableName)
|
||||||
|
|
||||||
session.engine.logger.Debug("[cache] Get SQL:", newsql, args)
|
session.engine.logger.Debug("[cache] Get SQL:", newsql, args)
|
||||||
table := session.statement.RefTable
|
table := session.statement.RefTable
|
||||||
|
|
|
@ -613,7 +613,7 @@ func (session *Session) cacheInsert(table string) error {
|
||||||
if !session.statement.UseCache {
|
if !session.statement.UseCache {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cacher := session.engine.GetCacher(table)
|
cacher := session.engine.cacherMgr.GetCacher(table)
|
||||||
if cacher == nil {
|
if cacher == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"xorm.io/xorm/internal/utils"
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -188,7 +189,7 @@ func (session *Session) isIndexExist2(tableName string, cols []string, unique bo
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, index := range indexes {
|
for _, index := range indexes {
|
||||||
if sliceEq(index.Cols, cols) {
|
if utils.SliceEq(index.Cols, cols) {
|
||||||
if unique {
|
if unique {
|
||||||
return index.Type == schemas.UniqueType, nil
|
return index.Type == schemas.UniqueType, nil
|
||||||
}
|
}
|
||||||
|
@ -241,7 +242,7 @@ func (session *Session) Sync2(beans ...interface{}) error {
|
||||||
|
|
||||||
for _, bean := range beans {
|
for _, bean := range beans {
|
||||||
v := rValue(bean)
|
v := rValue(bean)
|
||||||
table, err := engine.mapType(v)
|
table, err := engine.tagParser.MapType(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
|
"xorm.io/xorm/convert"
|
||||||
"xorm.io/xorm/dialects"
|
"xorm.io/xorm/dialects"
|
||||||
"xorm.io/xorm/internal/utils"
|
"xorm.io/xorm/internal/utils"
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
|
@ -329,7 +330,7 @@ func (statement *Statement) buildUpdates(bean interface{},
|
||||||
var val interface{}
|
var val interface{}
|
||||||
|
|
||||||
if fieldValue.CanAddr() {
|
if fieldValue.CanAddr() {
|
||||||
if structConvert, ok := fieldValue.Addr().Interface().(Conversion); ok {
|
if structConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok {
|
||||||
data, err := structConvert.ToDB()
|
data, err := structConvert.ToDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
engine.logger.Error(err)
|
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()
|
data, err := structConvert.ToDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
engine.logger.Error(err)
|
engine.logger.Error(err)
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
}
|
|
600
tag_test.go
600
tag_test.go
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package xorm
|
package tags
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -34,8 +34,8 @@ func splitTag(tag string) (tags []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// tagContext represents a context for xorm tag parse.
|
// Context represents a context for xorm tag parse.
|
||||||
type tagContext struct {
|
type Context struct {
|
||||||
tagName string
|
tagName string
|
||||||
params []string
|
params []string
|
||||||
preTag, nextTag string
|
preTag, nextTag string
|
||||||
|
@ -45,18 +45,18 @@ type tagContext struct {
|
||||||
isIndex bool
|
isIndex bool
|
||||||
isUnique bool
|
isUnique bool
|
||||||
indexNames map[string]int
|
indexNames map[string]int
|
||||||
engine *Engine
|
parser *Parser
|
||||||
hasCacheTag bool
|
hasCacheTag bool
|
||||||
hasNoCacheTag bool
|
hasNoCacheTag bool
|
||||||
ignoreNext bool
|
ignoreNext bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// tagHandler describes tag handler for XORM
|
// Handler describes tag handler for XORM
|
||||||
type tagHandler func(ctx *tagContext) error
|
type Handler func(ctx *Context) error
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// defaultTagHandlers enumerates all the default tag handler
|
// defaultTagHandlers enumerates all the default tag handler
|
||||||
defaultTagHandlers = map[string]tagHandler{
|
defaultTagHandlers = map[string]Handler{
|
||||||
"<-": OnlyFromDBTagHandler,
|
"<-": OnlyFromDBTagHandler,
|
||||||
"->": OnlyToDBTagHandler,
|
"->": OnlyToDBTagHandler,
|
||||||
"PK": PKTagHandler,
|
"PK": PKTagHandler,
|
||||||
|
@ -86,43 +86,43 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IgnoreTagHandler describes ignored tag handler
|
// IgnoreTagHandler describes ignored tag handler
|
||||||
func IgnoreTagHandler(ctx *tagContext) error {
|
func IgnoreTagHandler(ctx *Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnlyFromDBTagHandler describes mapping direction tag handler
|
// OnlyFromDBTagHandler describes mapping direction tag handler
|
||||||
func OnlyFromDBTagHandler(ctx *tagContext) error {
|
func OnlyFromDBTagHandler(ctx *Context) error {
|
||||||
ctx.col.MapType = schemas.ONLYFROMDB
|
ctx.col.MapType = schemas.ONLYFROMDB
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnlyToDBTagHandler describes mapping direction tag handler
|
// OnlyToDBTagHandler describes mapping direction tag handler
|
||||||
func OnlyToDBTagHandler(ctx *tagContext) error {
|
func OnlyToDBTagHandler(ctx *Context) error {
|
||||||
ctx.col.MapType = schemas.ONLYTODB
|
ctx.col.MapType = schemas.ONLYTODB
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PKTagHandler describes primary key tag handler
|
// PKTagHandler describes primary key tag handler
|
||||||
func PKTagHandler(ctx *tagContext) error {
|
func PKTagHandler(ctx *Context) error {
|
||||||
ctx.col.IsPrimaryKey = true
|
ctx.col.IsPrimaryKey = true
|
||||||
ctx.col.Nullable = false
|
ctx.col.Nullable = false
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NULLTagHandler describes null tag handler
|
// NULLTagHandler describes null tag handler
|
||||||
func NULLTagHandler(ctx *tagContext) error {
|
func NULLTagHandler(ctx *Context) error {
|
||||||
ctx.col.Nullable = (strings.ToUpper(ctx.preTag) != "NOT")
|
ctx.col.Nullable = (strings.ToUpper(ctx.preTag) != "NOT")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotNullTagHandler describes notnull tag handler
|
// NotNullTagHandler describes notnull tag handler
|
||||||
func NotNullTagHandler(ctx *tagContext) error {
|
func NotNullTagHandler(ctx *Context) error {
|
||||||
ctx.col.Nullable = false
|
ctx.col.Nullable = false
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutoIncrTagHandler describes autoincr tag handler
|
// AutoIncrTagHandler describes autoincr tag handler
|
||||||
func AutoIncrTagHandler(ctx *tagContext) error {
|
func AutoIncrTagHandler(ctx *Context) error {
|
||||||
ctx.col.IsAutoIncrement = true
|
ctx.col.IsAutoIncrement = true
|
||||||
/*
|
/*
|
||||||
if len(ctx.params) > 0 {
|
if len(ctx.params) > 0 {
|
||||||
|
@ -139,7 +139,7 @@ func AutoIncrTagHandler(ctx *tagContext) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultTagHandler describes default tag handler
|
// DefaultTagHandler describes default tag handler
|
||||||
func DefaultTagHandler(ctx *tagContext) error {
|
func DefaultTagHandler(ctx *Context) error {
|
||||||
if len(ctx.params) > 0 {
|
if len(ctx.params) > 0 {
|
||||||
ctx.col.Default = ctx.params[0]
|
ctx.col.Default = ctx.params[0]
|
||||||
} else {
|
} else {
|
||||||
|
@ -151,26 +151,26 @@ func DefaultTagHandler(ctx *tagContext) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreatedTagHandler describes created tag handler
|
// CreatedTagHandler describes created tag handler
|
||||||
func CreatedTagHandler(ctx *tagContext) error {
|
func CreatedTagHandler(ctx *Context) error {
|
||||||
ctx.col.IsCreated = true
|
ctx.col.IsCreated = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// VersionTagHandler describes version tag handler
|
// VersionTagHandler describes version tag handler
|
||||||
func VersionTagHandler(ctx *tagContext) error {
|
func VersionTagHandler(ctx *Context) error {
|
||||||
ctx.col.IsVersion = true
|
ctx.col.IsVersion = true
|
||||||
ctx.col.Default = "1"
|
ctx.col.Default = "1"
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UTCTagHandler describes utc tag handler
|
// UTCTagHandler describes utc tag handler
|
||||||
func UTCTagHandler(ctx *tagContext) error {
|
func UTCTagHandler(ctx *Context) error {
|
||||||
ctx.col.TimeZone = time.UTC
|
ctx.col.TimeZone = time.UTC
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalTagHandler describes local tag handler
|
// LocalTagHandler describes local tag handler
|
||||||
func LocalTagHandler(ctx *tagContext) error {
|
func LocalTagHandler(ctx *Context) error {
|
||||||
if len(ctx.params) == 0 {
|
if len(ctx.params) == 0 {
|
||||||
ctx.col.TimeZone = time.Local
|
ctx.col.TimeZone = time.Local
|
||||||
} else {
|
} else {
|
||||||
|
@ -184,19 +184,19 @@ func LocalTagHandler(ctx *tagContext) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatedTagHandler describes updated tag handler
|
// UpdatedTagHandler describes updated tag handler
|
||||||
func UpdatedTagHandler(ctx *tagContext) error {
|
func UpdatedTagHandler(ctx *Context) error {
|
||||||
ctx.col.IsUpdated = true
|
ctx.col.IsUpdated = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeletedTagHandler describes deleted tag handler
|
// DeletedTagHandler describes deleted tag handler
|
||||||
func DeletedTagHandler(ctx *tagContext) error {
|
func DeletedTagHandler(ctx *Context) error {
|
||||||
ctx.col.IsDeleted = true
|
ctx.col.IsDeleted = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IndexTagHandler describes index tag handler
|
// IndexTagHandler describes index tag handler
|
||||||
func IndexTagHandler(ctx *tagContext) error {
|
func IndexTagHandler(ctx *Context) error {
|
||||||
if len(ctx.params) > 0 {
|
if len(ctx.params) > 0 {
|
||||||
ctx.indexNames[ctx.params[0]] = schemas.IndexType
|
ctx.indexNames[ctx.params[0]] = schemas.IndexType
|
||||||
} else {
|
} else {
|
||||||
|
@ -206,7 +206,7 @@ func IndexTagHandler(ctx *tagContext) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UniqueTagHandler describes unique tag handler
|
// UniqueTagHandler describes unique tag handler
|
||||||
func UniqueTagHandler(ctx *tagContext) error {
|
func UniqueTagHandler(ctx *Context) error {
|
||||||
if len(ctx.params) > 0 {
|
if len(ctx.params) > 0 {
|
||||||
ctx.indexNames[ctx.params[0]] = schemas.UniqueType
|
ctx.indexNames[ctx.params[0]] = schemas.UniqueType
|
||||||
} else {
|
} else {
|
||||||
|
@ -216,7 +216,7 @@ func UniqueTagHandler(ctx *tagContext) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommentTagHandler add comment to column
|
// CommentTagHandler add comment to column
|
||||||
func CommentTagHandler(ctx *tagContext) error {
|
func CommentTagHandler(ctx *Context) error {
|
||||||
if len(ctx.params) > 0 {
|
if len(ctx.params) > 0 {
|
||||||
ctx.col.Comment = strings.Trim(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
|
// SQLTypeTagHandler describes SQL Type tag handler
|
||||||
func SQLTypeTagHandler(ctx *tagContext) error {
|
func SQLTypeTagHandler(ctx *Context) error {
|
||||||
ctx.col.SQLType = schemas.SQLType{Name: ctx.tagName}
|
ctx.col.SQLType = schemas.SQLType{Name: ctx.tagName}
|
||||||
if len(ctx.params) > 0 {
|
if len(ctx.params) > 0 {
|
||||||
if ctx.tagName == schemas.Enum {
|
if ctx.tagName == schemas.Enum {
|
||||||
|
@ -264,7 +264,7 @@ func SQLTypeTagHandler(ctx *tagContext) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtendsTagHandler describes extends tag handler
|
// ExtendsTagHandler describes extends tag handler
|
||||||
func ExtendsTagHandler(ctx *tagContext) error {
|
func ExtendsTagHandler(ctx *Context) error {
|
||||||
var fieldValue = ctx.fieldValue
|
var fieldValue = ctx.fieldValue
|
||||||
var isPtr = false
|
var isPtr = false
|
||||||
switch fieldValue.Kind() {
|
switch fieldValue.Kind() {
|
||||||
|
@ -280,7 +280,7 @@ func ExtendsTagHandler(ctx *tagContext) error {
|
||||||
isPtr = true
|
isPtr = true
|
||||||
fallthrough
|
fallthrough
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
parentTable, err := ctx.engine.mapType(fieldValue)
|
parentTable, err := ctx.parser.MapType(fieldValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -316,7 +316,7 @@ func ExtendsTagHandler(ctx *tagContext) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CacheTagHandler describes cache tag handler
|
// CacheTagHandler describes cache tag handler
|
||||||
func CacheTagHandler(ctx *tagContext) error {
|
func CacheTagHandler(ctx *Context) error {
|
||||||
if !ctx.hasCacheTag {
|
if !ctx.hasCacheTag {
|
||||||
ctx.hasCacheTag = true
|
ctx.hasCacheTag = true
|
||||||
}
|
}
|
||||||
|
@ -324,7 +324,7 @@ func CacheTagHandler(ctx *tagContext) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoCacheTagHandler describes nocache tag handler
|
// NoCacheTagHandler describes nocache tag handler
|
||||||
func NoCacheTagHandler(ctx *tagContext) error {
|
func NoCacheTagHandler(ctx *Context) error {
|
||||||
if !ctx.hasNoCacheTag {
|
if !ctx.hasNoCacheTag {
|
||||||
ctx.hasNoCacheTag = true
|
ctx.hasNoCacheTag = true
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -9,8 +9,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"xorm.io/xorm/convert"
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestArrayField(t *testing.T) {
|
func TestArrayField(t *testing.T) {
|
||||||
|
@ -138,7 +140,7 @@ type ConvStruct struct {
|
||||||
Conv2 *ConvString
|
Conv2 *ConvString
|
||||||
Cfg1 ConvConfig
|
Cfg1 ConvConfig
|
||||||
Cfg2 *ConvConfig `xorm:"TEXT"`
|
Cfg2 *ConvConfig `xorm:"TEXT"`
|
||||||
Cfg3 Conversion `xorm:"BLOB"`
|
Cfg3 convert.Conversion `xorm:"BLOB"`
|
||||||
Slice SliceType
|
Slice SliceType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +269,7 @@ type Status struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ Conversion = &Status{}
|
_ convert.Conversion = &Status{}
|
||||||
Registered Status = Status{"Registered", "white"}
|
Registered Status = Status{"Registered", "white"}
|
||||||
Approved Status = Status{"Approved", "green"}
|
Approved Status = Status{"Approved", "green"}
|
||||||
Removed Status = Status{"Removed", "red"}
|
Removed Status = Status{"Removed", "red"}
|
||||||
|
|
6
xorm.go
6
xorm.go
|
@ -21,6 +21,7 @@ import (
|
||||||
"xorm.io/xorm/log"
|
"xorm.io/xorm/log"
|
||||||
"xorm.io/xorm/names"
|
"xorm.io/xorm/names"
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
|
"xorm.io/xorm/tags"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -65,9 +66,7 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
|
||||||
dialect: dialect,
|
dialect: dialect,
|
||||||
Tables: make(map[reflect.Type]*schemas.Table),
|
Tables: make(map[reflect.Type]*schemas.Table),
|
||||||
mutex: &sync.RWMutex{},
|
mutex: &sync.RWMutex{},
|
||||||
TagIdentifier: "xorm",
|
|
||||||
TZLocation: time.Local,
|
TZLocation: time.Local,
|
||||||
tagHandlers: defaultTagHandlers,
|
|
||||||
defaultContext: context.Background(),
|
defaultContext: context.Background(),
|
||||||
cacherMgr: caches.NewManager(),
|
cacherMgr: caches.NewManager(),
|
||||||
}
|
}
|
||||||
|
@ -81,7 +80,8 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
|
||||||
logger := log.NewSimpleLogger(os.Stdout)
|
logger := log.NewSimpleLogger(os.Stdout)
|
||||||
logger.SetLevel(log.LOG_INFO)
|
logger.SetLevel(log.LOG_INFO)
|
||||||
engine.SetLogger(logger)
|
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)
|
runtime.SetFinalizer(engine, close)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue