xorm/tag.go

262 lines
5.7 KiB
Go
Raw Normal View History

// 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"
"reflect"
"strconv"
"strings"
"time"
"github.com/go-xorm/core"
)
type tagContext struct {
tagName string
params []string
preTag, nextTag string
table *core.Table
col *core.Column
fieldValue reflect.Value
isIndex bool
isUnique bool
indexNames map[string]int
engine *Engine
hasCacheTag bool
hasNoCacheTag bool
}
// tagHandler describes tag handler for XORM
type tagHandler func(ctx *tagContext) error
var (
// defaultTagHandlers enumerates all the default tag handler
defaultTagHandlers = map[string]tagHandler{
"<-": DirectTagHandler,
"->": DirectTagHandler,
"PK": PKTagHandler,
"NULL": NULLTagHandler,
"NOT": IgnoreTagHandler,
"AUTOINCR": AutoIncrTagHandler,
"DEFAULT": DefaultTagHandler,
"CREATED": CreatedTagHandler,
"UPDATED": UpdatedTagHandler,
"DELETED": DeletedTagHandler,
"VERSION": VersionTagHandler,
"UTC": UTCTagHandler,
"LOCAL": LocalTagHandler,
"NOTNULL": NotNullTagHandler,
"INDEX": IndexTagHandler,
"UNIQUE": UniqueTagHandler,
"CACHE": CacheTagHandler,
"NOCACHE": CacheTagHandler,
}
)
func init() {
for k, _ := range core.SqlTypes {
defaultTagHandlers[k] = SQLTypeTagHandler
}
}
func IgnoreTagHandler(ctx *tagContext) error {
return nil
}
// DirectTagHandler describes handle mapping type handler
func DirectTagHandler(ctx *tagContext) error {
if ctx.tagName == "<-" {
ctx.col.MapType = core.ONLYFROMDB
} else if ctx.tagName == "->" {
ctx.col.MapType = core.ONLYTODB
}
return nil
}
// PKTagHandler decribes handle pk
func PKTagHandler(ctx *tagContext) error {
ctx.col.IsPrimaryKey = true
ctx.col.Nullable = false
return nil
}
// NULLTagHandler
func NULLTagHandler(ctx *tagContext) error {
if len(ctx.preTag) == 0 {
ctx.col.Nullable = true
} else {
ctx.col.Nullable = (strings.ToUpper(ctx.preTag) != "NOT")
}
return nil
}
func NotNullTagHandler(ctx *tagContext) error {
ctx.col.Nullable = false
return nil
}
func AutoIncrTagHandler(ctx *tagContext) error {
ctx.col.IsAutoIncrement = true
//col.AutoIncrStart = 1
// TODO: for postgres how add autoincr?
/*case strings.HasPrefix(k, "AUTOINCR(") && strings.HasSuffix(k, ")"):
col.IsAutoIncrement = true
autoStart := k[len("AUTOINCR")+1 : len(k)-1]
autoStartInt, err := strconv.Atoi(autoStart)
if err != nil {
engine.LogError(err)
}
col.AutoIncrStart = autoStartInt*/
return nil
}
func DefaultTagHandler(ctx *tagContext) error {
ctx.col.Default = ctx.nextTag
return nil
}
func CreatedTagHandler(ctx *tagContext) error {
ctx.col.IsCreated = true
return nil
}
func VersionTagHandler(ctx *tagContext) error {
ctx.col.IsVersion = true
ctx.col.Default = "1"
return nil
}
func UTCTagHandler(ctx *tagContext) error {
ctx.col.TimeZone = time.UTC
return nil
}
func LocalTagHandler(ctx *tagContext) error {
if len(ctx.params) == 0 {
ctx.col.TimeZone = time.Local
} else {
var err error
ctx.col.TimeZone, err = time.LoadLocation(ctx.params[0])
if err != nil {
return err
}
}
return nil
}
func UpdatedTagHandler(ctx *tagContext) error {
ctx.col.IsUpdated = true
return nil
}
func DeletedTagHandler(ctx *tagContext) error {
ctx.col.IsDeleted = true
return nil
}
func IndexTagHandler(ctx *tagContext) error {
if len(ctx.params) > 0 {
ctx.indexNames[ctx.params[0]] = core.IndexType
} else {
ctx.isIndex = true
}
return nil
}
func UniqueTagHandler(ctx *tagContext) error {
if len(ctx.params) > 0 {
ctx.indexNames[ctx.params[0]] = core.UniqueType
} else {
ctx.isUnique = true
}
return nil
}
func SQLTypeTagHandler(ctx *tagContext) error {
ctx.col.SQLType = core.SQLType{Name: ctx.tagName}
if len(ctx.params) > 0 {
if ctx.tagName == core.Enum {
ctx.col.EnumOptions = make(map[string]int)
for k, v := range ctx.params {
v = strings.TrimSpace(v)
v = strings.Trim(v, "'")
ctx.col.EnumOptions[v] = k
}
} else if ctx.tagName == core.Set {
ctx.col.SetOptions = make(map[string]int)
for k, v := range ctx.params {
v = strings.TrimSpace(v)
v = strings.Trim(v, "'")
ctx.col.SetOptions[v] = k
}
} else {
var err error
if len(ctx.params) == 2 {
ctx.col.Length, err = strconv.Atoi(ctx.params[0])
if err != nil {
return err
}
ctx.col.Length2, err = strconv.Atoi(ctx.params[1])
if err != nil {
return err
}
} else if len(ctx.params) == 1 {
ctx.col.Length, err = strconv.Atoi(ctx.params[0])
if err != nil {
return err
}
}
}
}
return nil
}
func ExtendsTagHandler(ctx *tagContext) error {
var fieldValue = ctx.fieldValue
switch fieldValue.Kind() {
case reflect.Ptr:
f := fieldValue.Type().Elem()
if f.Kind() == reflect.Struct {
fieldPtr := fieldValue
fieldValue = fieldValue.Elem()
if !fieldValue.IsValid() || fieldPtr.IsNil() {
fieldValue = reflect.New(f).Elem()
}
}
fallthrough
case reflect.Struct:
parentTable, err := ctx.engine.mapType(fieldValue)
if err != nil {
return err
}
for _, col := range parentTable.Columns() {
col.FieldName = fmt.Sprintf("%v.%v", ctx.col.FieldName, col.FieldName)
ctx.table.AddColumn(col)
for indexName, indexType := range col.Indexes {
addIndex(indexName, ctx.table, col, indexType)
}
}
default:
//TODO: warning
}
return nil
}
func CacheTagHandler(ctx *tagContext) error {
if ctx.tagName == "CACHE" {
if !ctx.hasCacheTag {
ctx.hasCacheTag = true
}
} else if ctx.tagName == "NOCACHE" {
if !ctx.hasNoCacheTag {
ctx.hasNoCacheTag = true
}
}
return nil
}