You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

308 lines
7.5 KiB

  1. // Copyright 2020 The Xorm Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package tags
  5. import (
  6. "encoding/gob"
  7. "errors"
  8. "fmt"
  9. "reflect"
  10. "strings"
  11. "sync"
  12. "time"
  13. "xorm.io/xorm/caches"
  14. "xorm.io/xorm/convert"
  15. "xorm.io/xorm/dialects"
  16. "xorm.io/xorm/names"
  17. "xorm.io/xorm/schemas"
  18. )
  19. var (
  20. ErrUnsupportedType = errors.New("Unsupported type")
  21. )
  22. type Parser struct {
  23. identifier string
  24. dialect dialects.Dialect
  25. columnMapper names.Mapper
  26. tableMapper names.Mapper
  27. handlers map[string]Handler
  28. cacherMgr *caches.Manager
  29. tableCache sync.Map // map[reflect.Type]*schemas.Table
  30. }
  31. func NewParser(identifier string, dialect dialects.Dialect, tableMapper, columnMapper names.Mapper, cacherMgr *caches.Manager) *Parser {
  32. return &Parser{
  33. identifier: identifier,
  34. dialect: dialect,
  35. tableMapper: tableMapper,
  36. columnMapper: columnMapper,
  37. handlers: defaultTagHandlers,
  38. cacherMgr: cacherMgr,
  39. }
  40. }
  41. func (parser *Parser) GetTableMapper() names.Mapper {
  42. return parser.tableMapper
  43. }
  44. func (parser *Parser) SetTableMapper(mapper names.Mapper) {
  45. parser.ClearCaches()
  46. parser.tableMapper = mapper
  47. }
  48. func (parser *Parser) GetColumnMapper() names.Mapper {
  49. return parser.columnMapper
  50. }
  51. func (parser *Parser) SetColumnMapper(mapper names.Mapper) {
  52. parser.ClearCaches()
  53. parser.columnMapper = mapper
  54. }
  55. func (parser *Parser) ParseWithCache(v reflect.Value) (*schemas.Table, error) {
  56. t := v.Type()
  57. tableI, ok := parser.tableCache.Load(t)
  58. if ok {
  59. return tableI.(*schemas.Table), nil
  60. }
  61. table, err := parser.Parse(v)
  62. if err != nil {
  63. return nil, err
  64. }
  65. parser.tableCache.Store(t, table)
  66. if parser.cacherMgr.GetDefaultCacher() != nil {
  67. if v.CanAddr() {
  68. gob.Register(v.Addr().Interface())
  69. } else {
  70. gob.Register(v.Interface())
  71. }
  72. }
  73. return table, nil
  74. }
  75. // ClearCacheTable removes the database mapper of a type from the cache
  76. func (parser *Parser) ClearCacheTable(t reflect.Type) {
  77. parser.tableCache.Delete(t)
  78. }
  79. // ClearCaches removes all the cached table information parsed by structs
  80. func (parser *Parser) ClearCaches() {
  81. parser.tableCache = sync.Map{}
  82. }
  83. func addIndex(indexName string, table *schemas.Table, col *schemas.Column, indexType int) {
  84. if index, ok := table.Indexes[indexName]; ok {
  85. index.AddColumn(col.Name)
  86. col.Indexes[index.Name] = indexType
  87. } else {
  88. index := schemas.NewIndex(indexName, indexType)
  89. index.AddColumn(col.Name)
  90. table.AddIndex(index)
  91. col.Indexes[index.Name] = indexType
  92. }
  93. }
  94. // Parse parses a struct as a table information
  95. func (parser *Parser) Parse(v reflect.Value) (*schemas.Table, error) {
  96. t := v.Type()
  97. if t.Kind() == reflect.Ptr {
  98. t = t.Elem()
  99. v = v.Elem()
  100. }
  101. if t.Kind() != reflect.Struct {
  102. return nil, ErrUnsupportedType
  103. }
  104. table := schemas.NewEmptyTable()
  105. table.Type = t
  106. table.Name = names.GetTableName(parser.tableMapper, v)
  107. var idFieldColName string
  108. var hasCacheTag, hasNoCacheTag bool
  109. for i := 0; i < t.NumField(); i++ {
  110. tag := t.Field(i).Tag
  111. ormTagStr := tag.Get(parser.identifier)
  112. var col *schemas.Column
  113. fieldValue := v.Field(i)
  114. fieldType := fieldValue.Type()
  115. if ormTagStr != "" {
  116. col = &schemas.Column{
  117. FieldName: t.Field(i).Name,
  118. Nullable: true,
  119. IsPrimaryKey: false,
  120. IsAutoIncrement: false,
  121. MapType: schemas.TWOSIDES,
  122. Indexes: make(map[string]int),
  123. DefaultIsEmpty: true,
  124. }
  125. tags := splitTag(ormTagStr)
  126. if len(tags) > 0 {
  127. if tags[0] == "-" {
  128. continue
  129. }
  130. var ctx = Context{
  131. table: table,
  132. col: col,
  133. fieldValue: fieldValue,
  134. indexNames: make(map[string]int),
  135. parser: parser,
  136. }
  137. if strings.HasPrefix(strings.ToUpper(tags[0]), "EXTENDS") {
  138. pStart := strings.Index(tags[0], "(")
  139. if pStart > -1 && strings.HasSuffix(tags[0], ")") {
  140. var tagPrefix = strings.TrimFunc(tags[0][pStart+1:len(tags[0])-1], func(r rune) bool {
  141. return r == '\'' || r == '"'
  142. })
  143. ctx.params = []string{tagPrefix}
  144. }
  145. if err := ExtendsTagHandler(&ctx); err != nil {
  146. return nil, err
  147. }
  148. continue
  149. }
  150. for j, key := range tags {
  151. if ctx.ignoreNext {
  152. ctx.ignoreNext = false
  153. continue
  154. }
  155. k := strings.ToUpper(key)
  156. ctx.tagName = k
  157. ctx.params = []string{}
  158. pStart := strings.Index(k, "(")
  159. if pStart == 0 {
  160. return nil, errors.New("( could not be the first character")
  161. }
  162. if pStart > -1 {
  163. if !strings.HasSuffix(k, ")") {
  164. return nil, fmt.Errorf("field %s tag %s cannot match ) character", col.FieldName, key)
  165. }
  166. ctx.tagName = k[:pStart]
  167. ctx.params = strings.Split(key[pStart+1:len(k)-1], ",")
  168. }
  169. if j > 0 {
  170. ctx.preTag = strings.ToUpper(tags[j-1])
  171. }
  172. if j < len(tags)-1 {
  173. ctx.nextTag = tags[j+1]
  174. } else {
  175. ctx.nextTag = ""
  176. }
  177. if h, ok := parser.handlers[ctx.tagName]; ok {
  178. if err := h(&ctx); err != nil {
  179. return nil, err
  180. }
  181. } else {
  182. if strings.HasPrefix(key, "'") && strings.HasSuffix(key, "'") {
  183. col.Name = key[1 : len(key)-1]
  184. } else {
  185. col.Name = key
  186. }
  187. }
  188. if ctx.hasCacheTag {
  189. hasCacheTag = true
  190. }
  191. if ctx.hasNoCacheTag {
  192. hasNoCacheTag = true
  193. }
  194. }
  195. if col.SQLType.Name == "" {
  196. col.SQLType = schemas.Type2SQLType(fieldType)
  197. }
  198. parser.dialect.SQLType(col)
  199. if col.Length == 0 {
  200. col.Length = col.SQLType.DefaultLength
  201. }
  202. if col.Length2 == 0 {
  203. col.Length2 = col.SQLType.DefaultLength2
  204. }
  205. if col.Name == "" {
  206. col.Name = parser.columnMapper.Obj2Table(t.Field(i).Name)
  207. }
  208. if ctx.isUnique {
  209. ctx.indexNames[col.Name] = schemas.UniqueType
  210. } else if ctx.isIndex {
  211. ctx.indexNames[col.Name] = schemas.IndexType
  212. }
  213. for indexName, indexType := range ctx.indexNames {
  214. addIndex(indexName, table, col, indexType)
  215. }
  216. }
  217. } else {
  218. var sqlType schemas.SQLType
  219. if fieldValue.CanAddr() {
  220. if _, ok := fieldValue.Addr().Interface().(convert.Conversion); ok {
  221. sqlType = schemas.SQLType{Name: schemas.Text}
  222. }
  223. }
  224. if _, ok := fieldValue.Interface().(convert.Conversion); ok {
  225. sqlType = schemas.SQLType{Name: schemas.Text}
  226. } else {
  227. sqlType = schemas.Type2SQLType(fieldType)
  228. }
  229. col = schemas.NewColumn(parser.columnMapper.Obj2Table(t.Field(i).Name),
  230. t.Field(i).Name, sqlType, sqlType.DefaultLength,
  231. sqlType.DefaultLength2, true)
  232. if fieldType.Kind() == reflect.Int64 && (strings.ToUpper(col.FieldName) == "ID" || strings.HasSuffix(strings.ToUpper(col.FieldName), ".ID")) {
  233. idFieldColName = col.Name
  234. }
  235. }
  236. if col.IsAutoIncrement {
  237. col.Nullable = false
  238. }
  239. table.AddColumn(col)
  240. } // end for
  241. if idFieldColName != "" && len(table.PrimaryKeys) == 0 {
  242. col := table.GetColumn(idFieldColName)
  243. col.IsPrimaryKey = true
  244. col.IsAutoIncrement = true
  245. col.Nullable = false
  246. table.PrimaryKeys = append(table.PrimaryKeys, col.Name)
  247. table.AutoIncrement = col.Name
  248. }
  249. if hasCacheTag {
  250. if parser.cacherMgr.GetDefaultCacher() != nil { // !nash! use engine's cacher if provided
  251. //engine.logger.Info("enable cache on table:", table.Name)
  252. parser.cacherMgr.SetCacher(table.Name, parser.cacherMgr.GetDefaultCacher())
  253. } else {
  254. //engine.logger.Info("enable LRU cache on table:", table.Name)
  255. parser.cacherMgr.SetCacher(table.Name, caches.NewLRUCacher2(caches.NewMemoryStore(), time.Hour, 10000))
  256. }
  257. }
  258. if hasNoCacheTag {
  259. //engine.logger.Info("disable cache on table:", table.Name)
  260. parser.cacherMgr.SetCacher(table.Name, nil)
  261. }
  262. return table, nil
  263. }