Allows models like:
```
type User struct {
	Id    int64
	Name  string
	Email *string `orm:"null"`
}
```
This helps a lot when doing JSON marshalling/unmarshalling.
Pointer fields should always be declared with the NULL orm tag for sanity, this
probably requires documentation.
		
	
			
		
			
				
	
	
		
			208 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			208 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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"
 | |
| )
 | |
| 
 | |
| // get reflect.Type name with package path.
 | |
| func getFullName(typ reflect.Type) string {
 | |
| 	return typ.PkgPath() + "." + typ.Name()
 | |
| }
 | |
| 
 | |
| // get table name. method, or field name. auto snaked.
 | |
| func getTableName(val reflect.Value) string {
 | |
| 	ind := reflect.Indirect(val)
 | |
| 	fun := val.MethodByName("TableName")
 | |
| 	if fun.IsValid() {
 | |
| 		vals := fun.Call([]reflect.Value{})
 | |
| 		if len(vals) > 0 {
 | |
| 			val := vals[0]
 | |
| 			if val.Kind() == reflect.String {
 | |
| 				return val.String()
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return snakeString(ind.Type().Name())
 | |
| }
 | |
| 
 | |
| // get table engine, mysiam or innodb.
 | |
| func getTableEngine(val reflect.Value) string {
 | |
| 	fun := val.MethodByName("TableEngine")
 | |
| 	if fun.IsValid() {
 | |
| 		vals := fun.Call([]reflect.Value{})
 | |
| 		if len(vals) > 0 {
 | |
| 			val := vals[0]
 | |
| 			if val.Kind() == reflect.String {
 | |
| 				return val.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 {
 | |
| 			val := vals[0]
 | |
| 			if val.CanInterface() {
 | |
| 				if d, ok := val.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 {
 | |
| 			val := vals[0]
 | |
| 			if val.CanInterface() {
 | |
| 				if d, ok := val.Interface().([][]string); ok {
 | |
| 					return d
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // get snaked column name
 | |
| func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col string) string {
 | |
| 	column := col
 | |
| 	if col == "" {
 | |
| 		column = snakeString(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 = TypeCharField
 | |
| 	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 = TypeCharField
 | |
| 		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 = TypeCharField
 | |
| 			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) {
 | |
| 	attr := make(map[string]bool)
 | |
| 	tag := make(map[string]string)
 | |
| 	for _, v := range strings.Split(data, defaultStructTagDelim) {
 | |
| 		v = strings.TrimSpace(v)
 | |
| 		if supportTag[v] == 1 {
 | |
| 			attr[v] = true
 | |
| 		} else if i := strings.Index(v, "("); i > 0 && strings.Index(v, ")") == len(v)-1 {
 | |
| 			name := v[:i]
 | |
| 			if supportTag[name] == 2 {
 | |
| 				v = v[i+1 : len(v)-1]
 | |
| 				tag[name] = v
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	*attrs = attr
 | |
| 	*tags = tag
 | |
| }
 |