123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- package orm
- import (
- "reflect"
- "sync"
- "github.com/go-openapi/inflect"
- )
- type table struct {
- rt reflect.Type
- rv reflect.Value
- l *sync.RWMutex
- tFn, cFn MapperFn
- cols columns
- rels relations
- mapped bool
- }
- // getPrimaryKey tries to find primary key
- func (t *table) getPrimaryKey() *column {
- var pkey *column
- if c := t.hasTaggedColumn("primary"); c != nil {
- pkey = c
- } else if c := t.hasColumn("Id"); c != nil {
- pkey = c
- }
- return pkey
- }
- // hasTaggetColumn checks for a collumn tagget as
- func (t *table) hasTaggedColumn(ct string) *column {
- for _, col := range t.cols {
- if col.hasTag(ct) {
- return &col
- }
- }
- return nil
- }
- // hasColumn checks for a column by name
- func (t *table) hasColumn(c string) *column {
- for _, col := range t.cols {
- if col.ref == t.cFn(c) {
- return &col
- }
- }
- return nil
- }
- // addField from struct; both «relations» and «columns»
- // Communicates over a channel, to do relation mapping
- func (t *table) addField(f field, cbCh chan<- mapperCallback) {
- if f.hasTags("omit", "-") {
- return // Skip "omitted" and "dashed" fields
- }
- switch f.getType() {
- case Relation:
- // Make callback to itself, relations should be mapped after columns
- cbCh <- mapperCallback{
- from: t.getStructName(),
- to: t.getStructName(),
- fn: func(self *table) {
- // Get Primary Key
- pkey := t.getPrimaryKey()
- // Or at least try to
- if pkey == nil {
- return
- }
- // Predict related table name
- rtbl := f.getFieldType()
- // And check for tag
- if tag, ok := f.getTag("table"); ok {
- rtbl = tag
- }
- // Predict column name
- cn := f.getFieldName() + "Id"
- // And check for tag
- if tag, ok := f.getTag("fkey"); ok {
- cn = tag
- }
- // Check if it contains reference itself
- if c := self.hasColumn(cn); c != nil {
- // Make a call to load related table into scope;
- // we need its addr and must be loaded from mapper
- cbCh <- mapperCallback{
- from: self.getStructName(),
- to: rtbl,
- fn: func(tbl *table) {
- key := tbl.getPrimaryKey()
- self.rels.addRelation(belongsTo, relation{tbl, f, *key, *c})
- },
- }
- } else {
- // Or predict column name in related table
- cn = self.getStructName() + "Id"
- // Check for reference tag
- if tag, ok := f.getTag("ref"); ok {
- cn = tag
- }
- // Make a callback to the related table to check for relationg
- cbCh <- mapperCallback{
- from: self.getStructName(),
- to: rtbl,
- fn: func(tbl *table) {
- // Check for relation on column mane
- if c := tbl.hasColumn(cn); c != nil {
- // Predict the relations is «hasOne»
- has := hasOne
- // Try to predict (or simply guess) with pluralization, if «hasMany»
- if inflect.Pluralize(f.getFieldName()) == f.getFieldName() {
- has = hasMany
- }
- // Override with tagging if specified
- if tag, ok := f.getTag("has"); ok {
- switch tag {
- case "many":
- has = hasMany
- case "one":
- has = hasOne
- }
- }
- self.rels.addRelation(has, relation{tbl, f, *c, *pkey})
- }
- },
- }
- }
- return
- },
- }
- default: // Add column
- switch f.getKind() {
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- fallthrough // Support all Uint types
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- fallthrough // Support all Int types
- case reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
- fallthrough // Support all Float and Complex types
- case reflect.String, reflect.Bool:
- // Support string and boolean
- // Map column name
- dbf := t.cFn(f.getFieldName())
- // override with tagging
- if tag, ok := f.getTag("field"); ok {
- dbf = t.cFn(tag)
- }
- t.cols = append(t.cols, column{
- f, dbf,
- })
- }
- }
- }
- // getType returns the reflect.Type of the «table»
- func (t *table) getType() reflect.Type {
- return t.rt
- }
- // getValue returns the reflect.Value of the «table»
- func (t *table) getValue() reflect.Value {
- return t.rv
- }
- // isMapped returns true when columns is mapped
- // Not relations! They will be mapped in separate routines
- func (t *table) isMapped() bool {
- return t.mapped
- }
- // Lock read lock
- func (t *table) Lock() *table {
- t.l.RLock()
- return t
- }
- // Unlock read lock
- func (t *table) Unlock() {
- t.l.RUnlock()
- }
- // lock write lock
- func (t *table) lock() *table {
- t.l.Lock()
- return t
- }
- // unlock write lock
- func (t *table) unlock() {
- t.l.Unlock()
- }
- // getStructName returns the name of the struct
- func (t *table) getStructName() string {
- return t.getType().Name()
- }
- // getName returns the mapped table name
- // as identified in the database
- func (t *table) getName() string {
- return t.tFn(t.getType().Name())
- }
- // tables is simply a collection of tables
- type tables map[string]*table
|