table.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. package orm
  2. import (
  3. "reflect"
  4. "sync"
  5. "github.com/go-openapi/inflect"
  6. )
  7. type table struct {
  8. rt reflect.Type
  9. rv reflect.Value
  10. l *sync.RWMutex
  11. tFn, cFn MapperFn
  12. cols columns
  13. rels relations
  14. mapped bool
  15. }
  16. // getPrimaryKey tries to find primary key
  17. func (t *table) getPrimaryKey() *column {
  18. var pkey *column
  19. if c := t.hasTaggedColumn("primary"); c != nil {
  20. pkey = c
  21. } else if c := t.hasColumn("Id"); c != nil {
  22. pkey = c
  23. }
  24. return pkey
  25. }
  26. // hasTaggetColumn checks for a collumn tagget as
  27. func (t *table) hasTaggedColumn(ct string) *column {
  28. for _, col := range t.cols {
  29. if col.hasTag(ct) {
  30. return &col
  31. }
  32. }
  33. return nil
  34. }
  35. // hasColumn checks for a column by name
  36. func (t *table) hasColumn(c string) *column {
  37. for _, col := range t.cols {
  38. if col.ref == t.cFn(c) {
  39. return &col
  40. }
  41. }
  42. return nil
  43. }
  44. // addField from struct; both «relations» and «columns»
  45. // Communicates over a channel, to do relation mapping
  46. func (t *table) addField(f field, cbCh chan<- mapperCallback) {
  47. if f.hasTags("omit", "-") {
  48. return // Skip "omitted" and "dashed" fields
  49. }
  50. switch f.getType() {
  51. case Relation:
  52. // Make callback to itself, relations should be mapped after columns
  53. cbCh <- mapperCallback{
  54. from: t.getStructName(),
  55. to: t.getStructName(),
  56. fn: func(self *table) {
  57. // Get Primary Key
  58. pkey := t.getPrimaryKey()
  59. // Or at least try to
  60. if pkey == nil {
  61. return
  62. }
  63. // Predict related table name
  64. rtbl := f.getFieldType()
  65. // And check for tag
  66. if tag, ok := f.getTag("table"); ok {
  67. rtbl = tag
  68. }
  69. // Predict column name
  70. cn := f.getFieldName() + "Id"
  71. // And check for tag
  72. if tag, ok := f.getTag("fkey"); ok {
  73. cn = tag
  74. }
  75. // Check if it contains reference itself
  76. if c := self.hasColumn(cn); c != nil {
  77. // Make a call to load related table into scope;
  78. // we need its addr and must be loaded from mapper
  79. cbCh <- mapperCallback{
  80. from: self.getStructName(),
  81. to: rtbl,
  82. fn: func(tbl *table) {
  83. key := tbl.getPrimaryKey()
  84. self.rels.addRelation(belongsTo, relation{tbl, f, *key, *c})
  85. },
  86. }
  87. } else {
  88. // Or predict column name in related table
  89. cn = self.getStructName() + "Id"
  90. // Check for reference tag
  91. if tag, ok := f.getTag("ref"); ok {
  92. cn = tag
  93. }
  94. // Make a callback to the related table to check for relationg
  95. cbCh <- mapperCallback{
  96. from: self.getStructName(),
  97. to: rtbl,
  98. fn: func(tbl *table) {
  99. // Check for relation on column mane
  100. if c := tbl.hasColumn(cn); c != nil {
  101. // Predict the relations is «hasOne»
  102. has := hasOne
  103. // Try to predict (or simply guess) with pluralization, if «hasMany»
  104. if inflect.Pluralize(f.getFieldName()) == f.getFieldName() {
  105. has = hasMany
  106. }
  107. // Override with tagging if specified
  108. if tag, ok := f.getTag("has"); ok {
  109. switch tag {
  110. case "many":
  111. has = hasMany
  112. case "one":
  113. has = hasOne
  114. }
  115. }
  116. self.rels.addRelation(has, relation{tbl, f, *c, *pkey})
  117. }
  118. },
  119. }
  120. }
  121. return
  122. },
  123. }
  124. default: // Add column
  125. switch f.getKind() {
  126. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  127. fallthrough // Support all Uint types
  128. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  129. fallthrough // Support all Int types
  130. case reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
  131. fallthrough // Support all Float and Complex types
  132. case reflect.String, reflect.Bool:
  133. // Support string and boolean
  134. // Map column name
  135. dbf := t.cFn(f.getFieldName())
  136. // override with tagging
  137. if tag, ok := f.getTag("field"); ok {
  138. dbf = t.cFn(tag)
  139. }
  140. t.cols = append(t.cols, column{
  141. f, dbf,
  142. })
  143. }
  144. }
  145. }
  146. // getType returns the reflect.Type of the «table»
  147. func (t *table) getType() reflect.Type {
  148. return t.rt
  149. }
  150. // getValue returns the reflect.Value of the «table»
  151. func (t *table) getValue() reflect.Value {
  152. return t.rv
  153. }
  154. // isMapped returns true when columns is mapped
  155. // Not relations! They will be mapped in separate routines
  156. func (t *table) isMapped() bool {
  157. return t.mapped
  158. }
  159. // Lock read lock
  160. func (t *table) Lock() *table {
  161. t.l.RLock()
  162. return t
  163. }
  164. // Unlock read lock
  165. func (t *table) Unlock() {
  166. t.l.RUnlock()
  167. }
  168. // lock write lock
  169. func (t *table) lock() *table {
  170. t.l.Lock()
  171. return t
  172. }
  173. // unlock write lock
  174. func (t *table) unlock() {
  175. t.l.Unlock()
  176. }
  177. // getStructName returns the name of the struct
  178. func (t *table) getStructName() string {
  179. return t.getType().Name()
  180. }
  181. // getName returns the mapped table name
  182. // as identified in the database
  183. func (t *table) getName() string {
  184. return t.tFn(t.getType().Name())
  185. }
  186. // tables is simply a collection of tables
  187. type tables map[string]*table