123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- package orm
- import (
- "fmt"
- "math"
- "reflect"
- "strconv"
- "strings"
- "sync"
- )
- type table struct {
- rt reflect.Type
- rv reflect.Value
- l *sync.RWMutex
- tFn, cFn MapperFn
- cols columns
- rels relations
- mapped bool
- }
- func (t *table) Make() MappableInterface {
- return reflect.New(t.getType()).Interface().(MappableInterface)
- }
- func (t *table) CallMethod(i MappableInterface, n string, args ...interface{}) (ret []interface{}, err error) {
- if t.getValue(true).MethodByName(n).IsValid() {
- fn := reflect.ValueOf(i).MethodByName(n)
- fnt := fn.Type()
- in := []reflect.Value{}
- if fnt.IsVariadic() && len(args) < (fnt.NumIn()-1) {
- return ret, fmt.Errorf("To few arguments to «%s». Got «%d», expected «%d»", n, len(args), fnt.NumIn()-1)
- } else if !fnt.IsVariadic() && len(args) != fnt.NumIn() {
- return ret, fmt.Errorf("To few arguments to «%s». Got «%d», expected «%d»", n, len(args), fnt.NumIn()-1)
- }
- for x := 0; x < len(args); x++ {
- var inType reflect.Type
- if fnt.IsVariadic() && x >= fnt.NumIn()-1 {
- inType = fnt.In(fnt.NumIn() - 1).Elem()
- } else {
- inType = fnt.In(x)
- }
- argv := reflect.ValueOf(args[x])
- if !argv.IsValid() || !argv.Type().ConvertibleTo(inType) {
- switch inType.Kind() {
- case
- reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
- reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- var val uint64 = 0
- argx := args[x].([]uint8)
- for xi := len(argx) - 1; xi >= 0; xi-- {
- val += uint64(math.Pow(10, float64(len(argx)-xi-1))) * uint64(argx[xi]-'0')
- }
- args[x] = val
- return t.CallMethod(i, n, args...)
- case reflect.Float32, reflect.Float64:
- if val, err := strconv.ParseFloat(string(args[x].([]byte)), 64); err == nil {
- args[x] = val
- return t.CallMethod(i, n, args...)
- }
- case reflect.Complex64, reflect.Complex128:
- // Not implemented
- return ret, fmt.Errorf("Complex not implemented")
- }
- return ret, fmt.Errorf("Invalid argument to «%s». Got %s, expected %s", n, argv.String(), inType.String())
- }
- in = append(in, argv.Convert(inType))
- }
- var out []reflect.Value
- defer func() {
- if r := recover(); r != nil {
- fmt.Println("Recovered: ", r)
- err = fmt.Errorf("Recovered: %v", r)
- }
- }()
- out = fn.Call(in)[0:fnt.NumOut()]
- if strings.HasPrefix(n, "Get") {
- for _, val := range out {
- switch val.Kind() {
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- ret = append(ret, val.Uint())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- ret = append(ret, val.Int())
- case reflect.Float32, reflect.Float64:
- ret = append(ret, val.Float())
- case reflect.String:
- ret = append(ret, val.String())
- case reflect.Interface:
- if !val.IsNil() && val.CanInterface() && val.Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
- err = val.Interface().(error)
- }
- case reflect.Struct, reflect.Ptr:
- if val.CanInterface() && !val.IsNil() {
- ret = append(ret, val.Interface())
- } else if val.IsNil() {
- ret = append(ret, nil)
- }
- }
- }
- }
- return ret, err
- }
- return ret, fmt.Errorf("Invalid method «%s» on «%s»", n, t.getType())
- }
- // getPrimaryKey tries to find primary key
- func (t *table) getPrimaryKey() *column {
- var pkey *column = nil
- if c := t.hasTaggedColumn("primary"); c != nil {
- pkey = c
- } else if c := t.hasColumn("Id"); c != nil {
- pkey = c
- }
- return pkey
- }
- func (t *table) getRelations() map[relType][]relation {
- return t.rels.rmap
- }
- func (t *table) getColumns() columns {
- return t.cols
- }
- // 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 := self.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
- }
- /*
- if self.getStructName() == "PlatformCalendar" {
- fmt.Println(self.getStructName(), f.getFieldType(), cn)
- }
- */
- // 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()
- switch f.MakeType().Kind() {
- case reflect.Map, reflect.Slice:
- self.rels.addRelation(belongsTo, relation{tbl, f, *c, *key})
- default:
- 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 {
- has := hasOne
- switch f.MakeType().Kind() {
- case reflect.Map, reflect.Slice:
- has = hasMany
- }
- 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, reflect.Struct:
- // 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,
- })
- case reflect.Ptr:
- f.t = f.t.Elem()
- f.v = f.v.Elem()
- t.addField(f, cbCh)
- default:
- fmt.Println(t.getStructName(), "not supporting", f)
- }
- }
- }
- // 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(ptr ...bool) reflect.Value {
- if len(ptr) > 0 && ptr[0] {
- return t.rv
- }
- return t.rv.Elem()
- }
- // 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(q bool) string {
- if q {
- return SqlFlavor.Quote(t.getName(false))
- }
- return t.tFn(t.getType().Name())
- }
- func (t *table) getNameAs(q bool) string {
- return t.getName(q) + " AS " + t.getAlias(q)
- }
- func (t *table) getAlias(q bool) string {
- if q {
- return SqlFlavor.Quote(t.getAlias(false))
- }
- return t.getStructName()
- }
- // tables is simply a collection of tables
- type tables map[string]*table
|