@ -7,11 +7,11 @@ package tags
import (
"encoding/gob"
"errors"
"fmt"
"reflect"
"strings"
"sync"
"time"
"unicode"
"xorm.io/xorm/caches"
"xorm.io/xorm/convert"
@ -22,7 +22,7 @@ import (
var (
// ErrUnsupportedType represents an unsupported type error
ErrUnsupportedType = errors . New ( "U nsupported type" )
ErrUnsupportedType = errors . New ( "u nsupported type" )
)
// Parser represents a parser for xorm tag
@ -124,35 +124,37 @@ func addIndex(indexName string, table *schemas.Table, col *schemas.Column, index
}
}
// Parse parses a struct as a table information
func ( parser * Parser ) Parse ( v reflect . Value ) ( * schemas . Table , error ) {
t := v . Type ( )
if t . Kind ( ) == reflect . Ptr {
t = t . Elem ( )
v = v . Elem ( )
var ErrIgnoreField = errors . New ( "field will be ignored" )
func ( parser * Parser ) parseFieldWithNoTag ( fieldIndex int , field reflect . StructField , fieldValue reflect . Value ) ( * schemas . Column , error ) {
var sqlType schemas . SQLType
if fieldValue . CanAddr ( ) {
if _ , ok := fieldValue . Addr ( ) . Interface ( ) . ( convert . Conversion ) ; ok {
sqlType = schemas . SQLType { Name : schemas . Text }
}
if t . Kind ( ) != reflect . Struct {
return nil , ErrUnsupportedType
}
if _ , ok := fieldValue . Interface ( ) . ( convert . Conversion ) ; ok {
sqlType = schemas . SQLType { Name : schemas . Text }
} else {
sqlType = schemas . Type2SQLType ( field . Type )
}
col := schemas . NewColumn ( parser . columnMapper . Obj2Table ( field . Name ) ,
field . Name , sqlType , sqlType . DefaultLength ,
sqlType . DefaultLength2 , true )
col . FieldIndex = [ ] int { fieldIndex }
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 field . Type . Kind ( ) == reflect . Int64 && ( strings . ToUpper ( col . FieldName ) == "ID" || strings . HasSuffix ( strings . ToUpper ( col . FieldName ) , ".ID" ) ) {
col . IsAutoIncrement = true
col . IsPrimaryKey = true
col . Nullable = false
}
return col , nil
}
if ormTagStr != "" {
col = & schemas . Column {
FieldName : t . Field ( i ) . Name ,
func ( parser * Parser ) parseFieldWithTags ( table * schemas . Table , fieldIndex int , field reflect . StructField , fieldValue reflect . Value , tags [ ] tag ) ( * schemas . Column , error ) {
var col = & schemas . Column {
FieldName : field . Name ,
FieldIndex : [ ] int { fieldIndex } ,
Nullable : true ,
IsPrimaryKey : false ,
IsAutoIncrement : false ,
@ -160,12 +162,6 @@ func (parser *Parser) Parse(v reflect.Value) (*schemas.Table, error) {
Indexes : make ( map [ string ] int ) ,
DefaultIsEmpty : true ,
}
tags := splitTag ( ormTagStr )
if len ( tags ) > 0 {
if tags [ 0 ] == "-" {
continue
}
var ctx = Context {
table : table ,
@ -175,76 +171,50 @@ func (parser *Parser) Parse(v reflect.Value) (*schemas.Table, error) {
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 {
for j , tag := 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 ] , "," )
}
ctx . tag = tag
ctx . tagUname = strings . ToUpper ( tag . name )
if j > 0 {
ctx . preTag = strings . ToUpper ( tags [ j - 1 ] )
ctx . preTag = strings . ToUpper ( tags [ j - 1 ] . name )
}
if j < len ( tags ) - 1 {
ctx . nextTag = tags [ j + 1 ]
ctx . nextTag = tags [ j + 1 ] . name
} else {
ctx . nextTag = ""
}
if h , ok := parser . handlers [ ctx . tagN ame ] ; ok {
if h , ok := parser . handlers [ ctx . tagUname ] ; 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 ]
if strings . HasPrefix ( ctx . tag . name , "'" ) && strings . HasSuffix ( ctx . tag . name , "'" ) {
col . Name = ctx . tag . name [ 1 : len ( ctx . tag . name ) - 1 ]
} else {
col . Name = key
col . Name = ctx . tag . name
}
}
if ctx . hasCacheTag {
hasCacheTag = true
if parser . cacherMgr . GetDefaultCacher ( ) != nil {
parser . cacherMgr . SetCacher ( table . Name , parser . cacherMgr . GetDefaultCacher ( ) )
} else {
parser . cacherMgr . SetCacher ( table . Name , caches . NewLRUCacher2 ( caches . NewMemoryStore ( ) , time . Hour , 10000 ) )
}
}
if ctx . hasNoCacheTag {
hasNoCacheTag = true
parser . cacherMgr . SetCacher ( table . Name , nil )
}
}
if col . SQLType . Name == "" {
col . SQLType = schemas . Type2SQLType ( fieldType )
col . SQLType = schemas . Type2SQLType ( field . Type )
}
parser . dialect . SQLType ( col )
if col . Length == 0 {
@ -254,7 +224,7 @@ func (parser *Parser) Parse(v reflect.Value) (*schemas.Table, error) {
col . Length2 = col . SQLType . DefaultLength2
}
if col . Name == "" {
col . Name = parser . columnMapper . Obj2Table ( t . F ield( i ) . Name )
col . Name = parser . columnMapper . Obj2Table ( f ield. Name )
}
if ctx . isUnique {
@ -266,58 +236,70 @@ func (parser *Parser) Parse(v reflect.Value) (*schemas.Table, error) {
for indexName , indexType := range ctx . indexNames {
addIndex ( indexName , table , col , indexType )
}
return col , nil
}
func ( parser * Parser ) parseField ( table * schemas . Table , fieldIndex int , field reflect . StructField , fieldValue reflect . Value ) ( * schemas . Column , error ) {
var (
tag = field . Tag
ormTagStr = strings . TrimSpace ( tag . Get ( parser . identifier ) )
)
if ormTagStr == "-" {
return nil , ErrIgnoreField
}
} else if fieldValue . CanSet ( ) {
var sqlType schemas . SQLType
if fieldValue . CanAddr ( ) {
if _ , ok := fieldValue . Addr ( ) . Interface ( ) . ( convert . Conversion ) ; ok {
sqlType = schemas . SQLType { Name : schemas . Text }
if ormTagStr == "" {
return parser . parseFieldWithNoTag ( fieldIndex , field , fieldValue )
}
tags , err := splitTag ( ormTagStr )
if err != nil {
return nil , err
}
if _ , ok := fieldValue . Interface ( ) . ( convert . Conversion ) ; ok {
sqlType = schemas . SQLType { Name : schemas . Text }
} else {
sqlType = schemas . Type2SQLType ( fieldType )
return parser . parseFieldWithTags ( table , fieldIndex , field , fieldValue , tags )
}
func isNotTitle ( n string ) bool {
for _ , c := range n {
return unicode . IsLower ( c )
}
col = schemas . NewColumn ( parser . columnMapper . Obj2Table ( t . Field ( i ) . Name ) ,
t . Field ( i ) . Name , sqlType , sqlType . DefaultLength ,
sqlType . DefaultLength2 , true )
return true
}
if fieldType . Kind ( ) == reflect . Int64 && ( strings . ToUpper ( col . FieldName ) == "ID" || strings . HasSuffix ( strings . ToUpper ( col . FieldName ) , ".ID" ) ) {
idFieldColName = col . Name
// Parse parses a struct as a table information
func ( parser * Parser ) Parse ( v reflect . Value ) ( * schemas . Table , error ) {
t := v . Type ( )
if t . Kind ( ) == reflect . Ptr {
t = t . Elem ( )
v = v . Elem ( )
}
} else {
if t . Kind ( ) != reflect . Struct {
return nil , ErrUnsupportedType
}
table := schemas . NewEmptyTable ( )
table . Type = t
table . Name = names . GetTableName ( parser . tableMapper , v )
for i := 0 ; i < t . NumField ( ) ; i ++ {
var field = t . Field ( i )
if isNotTitle ( field . Name ) {
continue
}
if col . IsAutoIncrement {
col . Nullable = false
col , err := parser . parseField ( table , i , field , v . Field ( i ) )
if err == ErrIgnoreField {
continue
} else if err != nil {
return nil , err
}
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 )
deletedColumn := table . DeletedColumn ( )
// check columns
if deletedColumn != nil {
deletedColumn . Nullable = true
}
return table , nil