// Copyright 2014 beego Author. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package orm import ( "database/sql" "fmt" "reflect" "strings" "time" ) // 1 is attr // 2 is tag var supportTag = map[string]int{ "-": 1, "null": 1, "index": 1, "unique": 1, "pk": 1, "auto": 1, "auto_now": 1, "auto_now_add": 1, "size": 2, "column": 2, "default": 2, "rel": 2, "reverse": 2, "rel_table": 2, "rel_through": 2, "digits": 2, "decimals": 2, "on_delete": 2, "type": 2, "description": 2, "precision": 2, } // get reflect.Type name with package path. func getFullName(typ reflect.Type) string { return typ.PkgPath() + "." + typ.Name() } // getTableName get struct table name. // If the struct implement the TableName, then get the result as tablename // else use the struct name which will apply snakeString. func getTableName(val reflect.Value) string { if fun := val.MethodByName("TableName"); fun.IsValid() { vals := fun.Call([]reflect.Value{}) // has return and the first val is string if len(vals) > 0 && vals[0].Kind() == reflect.String { return vals[0].String() } } return snakeString(reflect.Indirect(val).Type().Name()) } // get table engine, myisam or innodb. func getTableEngine(val reflect.Value) string { fun := val.MethodByName("TableEngine") if fun.IsValid() { vals := fun.Call([]reflect.Value{}) if len(vals) > 0 && vals[0].Kind() == reflect.String { return vals[0].String() } } return "" } // get table index from method. func getTableIndex(val reflect.Value) [][]string { fun := val.MethodByName("TableIndex") if fun.IsValid() { vals := fun.Call([]reflect.Value{}) if len(vals) > 0 && vals[0].CanInterface() { if d, ok := vals[0].Interface().([][]string); ok { return d } } } return nil } // get table unique from method func getTableUnique(val reflect.Value) [][]string { fun := val.MethodByName("TableUnique") if fun.IsValid() { vals := fun.Call([]reflect.Value{}) if len(vals) > 0 && vals[0].CanInterface() { if d, ok := vals[0].Interface().([][]string); ok { return d } } } return nil } // get whether the table needs to be created for the database alias func isApplicableTableForDB(val reflect.Value, db string) bool { if !val.IsValid() { return true } fun := val.MethodByName("IsApplicableTableForDB") if fun.IsValid() { vals := fun.Call([]reflect.Value{reflect.ValueOf(db)}) if len(vals) > 0 && vals[0].Kind() == reflect.Bool { return vals[0].Bool() } } return true } // get snaked column name func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col string) string { column := col if col == "" { column = nameStrategyMap[nameStrategy](sf.Name) } switch ft { case RelForeignKey, RelOneToOne: if len(col) == 0 { column = column + "_id" } case RelManyToMany, RelReverseMany, RelReverseOne: column = sf.Name } return column } // return field type as type constant from reflect.Value func getFieldType(val reflect.Value) (ft int, err error) { switch val.Type() { case reflect.TypeOf(new(int8)): ft = TypeBitField case reflect.TypeOf(new(int16)): ft = TypeSmallIntegerField case reflect.TypeOf(new(int32)), reflect.TypeOf(new(int)): ft = TypeIntegerField case reflect.TypeOf(new(int64)): ft = TypeBigIntegerField case reflect.TypeOf(new(uint8)): ft = TypePositiveBitField case reflect.TypeOf(new(uint16)): ft = TypePositiveSmallIntegerField case reflect.TypeOf(new(uint32)), reflect.TypeOf(new(uint)): ft = TypePositiveIntegerField case reflect.TypeOf(new(uint64)): ft = TypePositiveBigIntegerField case reflect.TypeOf(new(float32)), reflect.TypeOf(new(float64)): ft = TypeFloatField case reflect.TypeOf(new(bool)): ft = TypeBooleanField case reflect.TypeOf(new(string)): ft = TypeVarCharField case reflect.TypeOf(new(time.Time)): ft = TypeDateTimeField default: elm := reflect.Indirect(val) switch elm.Kind() { case reflect.Int8: ft = TypeBitField case reflect.Int16: ft = TypeSmallIntegerField case reflect.Int32, reflect.Int: ft = TypeIntegerField case reflect.Int64: ft = TypeBigIntegerField case reflect.Uint8: ft = TypePositiveBitField case reflect.Uint16: ft = TypePositiveSmallIntegerField case reflect.Uint32, reflect.Uint: ft = TypePositiveIntegerField case reflect.Uint64: ft = TypePositiveBigIntegerField case reflect.Float32, reflect.Float64: ft = TypeFloatField case reflect.Bool: ft = TypeBooleanField case reflect.String: ft = TypeVarCharField default: if elm.Interface() == nil { panic(fmt.Errorf("%s is nil pointer, may be miss setting tag", val)) } switch elm.Interface().(type) { case sql.NullInt64: ft = TypeBigIntegerField case sql.NullFloat64: ft = TypeFloatField case sql.NullBool: ft = TypeBooleanField case sql.NullString: ft = TypeVarCharField case time.Time: ft = TypeDateTimeField } } } if ft&IsFieldType == 0 { err = fmt.Errorf("unsupport field type %s, may be miss setting tag", val) } return } // parse struct tag string func parseStructTag(data string) (attrs map[string]bool, tags map[string]string) { attrs = make(map[string]bool) tags = make(map[string]string) for _, v := range strings.Split(data, defaultStructTagDelim) { if v == "" { continue } v = strings.TrimSpace(v) if t := strings.ToLower(v); supportTag[t] == 1 { attrs[t] = true } else if i := strings.Index(v, "("); i > 0 && strings.Index(v, ")") == len(v)-1 { name := t[:i] if supportTag[name] == 2 { v = v[i+1 : len(v)-1] tags[name] = v } } else { DebugLog.Println("unsupport orm tag", v) } } return }