github.com/astaxie/beego@v1.12.3/orm/models_utils.go (about)

     1  // Copyright 2014 beego Author. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package orm
    16  
    17  import (
    18  	"database/sql"
    19  	"fmt"
    20  	"reflect"
    21  	"strings"
    22  	"time"
    23  )
    24  
    25  // 1 is attr
    26  // 2 is tag
    27  var supportTag = map[string]int{
    28  	"-":            1,
    29  	"null":         1,
    30  	"index":        1,
    31  	"unique":       1,
    32  	"pk":           1,
    33  	"auto":         1,
    34  	"auto_now":     1,
    35  	"auto_now_add": 1,
    36  	"size":         2,
    37  	"column":       2,
    38  	"default":      2,
    39  	"rel":          2,
    40  	"reverse":      2,
    41  	"rel_table":    2,
    42  	"rel_through":  2,
    43  	"digits":       2,
    44  	"decimals":     2,
    45  	"on_delete":    2,
    46  	"type":         2,
    47  	"description":  2,
    48  }
    49  
    50  // get reflect.Type name with package path.
    51  func getFullName(typ reflect.Type) string {
    52  	return typ.PkgPath() + "." + typ.Name()
    53  }
    54  
    55  // getTableName get struct table name.
    56  // If the struct implement the TableName, then get the result as tablename
    57  // else use the struct name which will apply snakeString.
    58  func getTableName(val reflect.Value) string {
    59  	if fun := val.MethodByName("TableName"); fun.IsValid() {
    60  		vals := fun.Call([]reflect.Value{})
    61  		// has return and the first val is string
    62  		if len(vals) > 0 && vals[0].Kind() == reflect.String {
    63  			return vals[0].String()
    64  		}
    65  	}
    66  	return snakeString(reflect.Indirect(val).Type().Name())
    67  }
    68  
    69  // get table engine, myisam or innodb.
    70  func getTableEngine(val reflect.Value) string {
    71  	fun := val.MethodByName("TableEngine")
    72  	if fun.IsValid() {
    73  		vals := fun.Call([]reflect.Value{})
    74  		if len(vals) > 0 && vals[0].Kind() == reflect.String {
    75  			return vals[0].String()
    76  		}
    77  	}
    78  	return ""
    79  }
    80  
    81  // get table index from method.
    82  func getTableIndex(val reflect.Value) [][]string {
    83  	fun := val.MethodByName("TableIndex")
    84  	if fun.IsValid() {
    85  		vals := fun.Call([]reflect.Value{})
    86  		if len(vals) > 0 && vals[0].CanInterface() {
    87  			if d, ok := vals[0].Interface().([][]string); ok {
    88  				return d
    89  			}
    90  		}
    91  	}
    92  	return nil
    93  }
    94  
    95  // get table unique from method
    96  func getTableUnique(val reflect.Value) [][]string {
    97  	fun := val.MethodByName("TableUnique")
    98  	if fun.IsValid() {
    99  		vals := fun.Call([]reflect.Value{})
   100  		if len(vals) > 0 && vals[0].CanInterface() {
   101  			if d, ok := vals[0].Interface().([][]string); ok {
   102  				return d
   103  			}
   104  		}
   105  	}
   106  	return nil
   107  }
   108  
   109  // get snaked column name
   110  func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col string) string {
   111  	column := col
   112  	if col == "" {
   113  		column = nameStrategyMap[nameStrategy](sf.Name)
   114  	}
   115  	switch ft {
   116  	case RelForeignKey, RelOneToOne:
   117  		if len(col) == 0 {
   118  			column = column + "_id"
   119  		}
   120  	case RelManyToMany, RelReverseMany, RelReverseOne:
   121  		column = sf.Name
   122  	}
   123  	return column
   124  }
   125  
   126  // return field type as type constant from reflect.Value
   127  func getFieldType(val reflect.Value) (ft int, err error) {
   128  	switch val.Type() {
   129  	case reflect.TypeOf(new(int8)):
   130  		ft = TypeBitField
   131  	case reflect.TypeOf(new(int16)):
   132  		ft = TypeSmallIntegerField
   133  	case reflect.TypeOf(new(int32)),
   134  		reflect.TypeOf(new(int)):
   135  		ft = TypeIntegerField
   136  	case reflect.TypeOf(new(int64)):
   137  		ft = TypeBigIntegerField
   138  	case reflect.TypeOf(new(uint8)):
   139  		ft = TypePositiveBitField
   140  	case reflect.TypeOf(new(uint16)):
   141  		ft = TypePositiveSmallIntegerField
   142  	case reflect.TypeOf(new(uint32)),
   143  		reflect.TypeOf(new(uint)):
   144  		ft = TypePositiveIntegerField
   145  	case reflect.TypeOf(new(uint64)):
   146  		ft = TypePositiveBigIntegerField
   147  	case reflect.TypeOf(new(float32)),
   148  		reflect.TypeOf(new(float64)):
   149  		ft = TypeFloatField
   150  	case reflect.TypeOf(new(bool)):
   151  		ft = TypeBooleanField
   152  	case reflect.TypeOf(new(string)):
   153  		ft = TypeVarCharField
   154  	case reflect.TypeOf(new(time.Time)):
   155  		ft = TypeDateTimeField
   156  	default:
   157  		elm := reflect.Indirect(val)
   158  		switch elm.Kind() {
   159  		case reflect.Int8:
   160  			ft = TypeBitField
   161  		case reflect.Int16:
   162  			ft = TypeSmallIntegerField
   163  		case reflect.Int32, reflect.Int:
   164  			ft = TypeIntegerField
   165  		case reflect.Int64:
   166  			ft = TypeBigIntegerField
   167  		case reflect.Uint8:
   168  			ft = TypePositiveBitField
   169  		case reflect.Uint16:
   170  			ft = TypePositiveSmallIntegerField
   171  		case reflect.Uint32, reflect.Uint:
   172  			ft = TypePositiveIntegerField
   173  		case reflect.Uint64:
   174  			ft = TypePositiveBigIntegerField
   175  		case reflect.Float32, reflect.Float64:
   176  			ft = TypeFloatField
   177  		case reflect.Bool:
   178  			ft = TypeBooleanField
   179  		case reflect.String:
   180  			ft = TypeVarCharField
   181  		default:
   182  			if elm.Interface() == nil {
   183  				panic(fmt.Errorf("%s is nil pointer, may be miss setting tag", val))
   184  			}
   185  			switch elm.Interface().(type) {
   186  			case sql.NullInt64:
   187  				ft = TypeBigIntegerField
   188  			case sql.NullFloat64:
   189  				ft = TypeFloatField
   190  			case sql.NullBool:
   191  				ft = TypeBooleanField
   192  			case sql.NullString:
   193  				ft = TypeVarCharField
   194  			case time.Time:
   195  				ft = TypeDateTimeField
   196  			}
   197  		}
   198  	}
   199  	if ft&IsFieldType == 0 {
   200  		err = fmt.Errorf("unsupport field type %s, may be miss setting tag", val)
   201  	}
   202  	return
   203  }
   204  
   205  // parse struct tag string
   206  func parseStructTag(data string) (attrs map[string]bool, tags map[string]string) {
   207  	attrs = make(map[string]bool)
   208  	tags = make(map[string]string)
   209  	for _, v := range strings.Split(data, defaultStructTagDelim) {
   210  		if v == "" {
   211  			continue
   212  		}
   213  		v = strings.TrimSpace(v)
   214  		if t := strings.ToLower(v); supportTag[t] == 1 {
   215  			attrs[t] = true
   216  		} else if i := strings.Index(v, "("); i > 0 && strings.Index(v, ")") == len(v)-1 {
   217  			name := t[:i]
   218  			if supportTag[name] == 2 {
   219  				v = v[i+1 : len(v)-1]
   220  				tags[name] = v
   221  			}
   222  		} else {
   223  			DebugLog.Println("unsupport orm tag", v)
   224  		}
   225  	}
   226  	return
   227  }