github.com/astaxie/beego@v1.12.3/orm/models_info_f.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  	"errors"
    19  	"fmt"
    20  	"reflect"
    21  	"strings"
    22  )
    23  
    24  var errSkipField = errors.New("skip field")
    25  
    26  // field info collection
    27  type fields struct {
    28  	pk            *fieldInfo
    29  	columns       map[string]*fieldInfo
    30  	fields        map[string]*fieldInfo
    31  	fieldsLow     map[string]*fieldInfo
    32  	fieldsByType  map[int][]*fieldInfo
    33  	fieldsRel     []*fieldInfo
    34  	fieldsReverse []*fieldInfo
    35  	fieldsDB      []*fieldInfo
    36  	rels          []*fieldInfo
    37  	orders        []string
    38  	dbcols        []string
    39  }
    40  
    41  // add field info
    42  func (f *fields) Add(fi *fieldInfo) (added bool) {
    43  	if f.fields[fi.name] == nil && f.columns[fi.column] == nil {
    44  		f.columns[fi.column] = fi
    45  		f.fields[fi.name] = fi
    46  		f.fieldsLow[strings.ToLower(fi.name)] = fi
    47  	} else {
    48  		return
    49  	}
    50  	if _, ok := f.fieldsByType[fi.fieldType]; !ok {
    51  		f.fieldsByType[fi.fieldType] = make([]*fieldInfo, 0)
    52  	}
    53  	f.fieldsByType[fi.fieldType] = append(f.fieldsByType[fi.fieldType], fi)
    54  	f.orders = append(f.orders, fi.column)
    55  	if fi.dbcol {
    56  		f.dbcols = append(f.dbcols, fi.column)
    57  		f.fieldsDB = append(f.fieldsDB, fi)
    58  	}
    59  	if fi.rel {
    60  		f.fieldsRel = append(f.fieldsRel, fi)
    61  	}
    62  	if fi.reverse {
    63  		f.fieldsReverse = append(f.fieldsReverse, fi)
    64  	}
    65  	return true
    66  }
    67  
    68  // get field info by name
    69  func (f *fields) GetByName(name string) *fieldInfo {
    70  	return f.fields[name]
    71  }
    72  
    73  // get field info by column name
    74  func (f *fields) GetByColumn(column string) *fieldInfo {
    75  	return f.columns[column]
    76  }
    77  
    78  // get field info by string, name is prior
    79  func (f *fields) GetByAny(name string) (*fieldInfo, bool) {
    80  	if fi, ok := f.fields[name]; ok {
    81  		return fi, ok
    82  	}
    83  	if fi, ok := f.fieldsLow[strings.ToLower(name)]; ok {
    84  		return fi, ok
    85  	}
    86  	if fi, ok := f.columns[name]; ok {
    87  		return fi, ok
    88  	}
    89  	return nil, false
    90  }
    91  
    92  // create new field info collection
    93  func newFields() *fields {
    94  	f := new(fields)
    95  	f.fields = make(map[string]*fieldInfo)
    96  	f.fieldsLow = make(map[string]*fieldInfo)
    97  	f.columns = make(map[string]*fieldInfo)
    98  	f.fieldsByType = make(map[int][]*fieldInfo)
    99  	return f
   100  }
   101  
   102  // single field info
   103  type fieldInfo struct {
   104  	mi                  *modelInfo
   105  	fieldIndex          []int
   106  	fieldType           int
   107  	dbcol               bool // table column fk and onetoone
   108  	inModel             bool
   109  	name                string
   110  	fullName            string
   111  	column              string
   112  	addrValue           reflect.Value
   113  	sf                  reflect.StructField
   114  	auto                bool
   115  	pk                  bool
   116  	null                bool
   117  	index               bool
   118  	unique              bool
   119  	colDefault          bool  // whether has default tag
   120  	initial             StrTo // store the default value
   121  	size                int
   122  	toText              bool
   123  	autoNow             bool
   124  	autoNowAdd          bool
   125  	rel                 bool // if type equal to RelForeignKey, RelOneToOne, RelManyToMany then true
   126  	reverse             bool
   127  	reverseField        string
   128  	reverseFieldInfo    *fieldInfo
   129  	reverseFieldInfoTwo *fieldInfo
   130  	reverseFieldInfoM2M *fieldInfo
   131  	relTable            string
   132  	relThrough          string
   133  	relThroughModelInfo *modelInfo
   134  	relModelInfo        *modelInfo
   135  	digits              int
   136  	decimals            int
   137  	isFielder           bool // implement Fielder interface
   138  	onDelete            string
   139  	description         string
   140  }
   141  
   142  // new field info
   143  func newFieldInfo(mi *modelInfo, field reflect.Value, sf reflect.StructField, mName string) (fi *fieldInfo, err error) {
   144  	var (
   145  		tag       string
   146  		tagValue  string
   147  		initial   StrTo // store the default value
   148  		fieldType int
   149  		attrs     map[string]bool
   150  		tags      map[string]string
   151  		addrField reflect.Value
   152  	)
   153  
   154  	fi = new(fieldInfo)
   155  
   156  	// if field which CanAddr is the follow type
   157  	//  A value is addressable if it is an element of a slice,
   158  	//  an element of an addressable array, a field of an
   159  	//  addressable struct, or the result of dereferencing a pointer.
   160  	addrField = field
   161  	if field.CanAddr() && field.Kind() != reflect.Ptr {
   162  		addrField = field.Addr()
   163  		if _, ok := addrField.Interface().(Fielder); !ok {
   164  			if field.Kind() == reflect.Slice {
   165  				addrField = field
   166  			}
   167  		}
   168  	}
   169  
   170  	attrs, tags = parseStructTag(sf.Tag.Get(defaultStructTagName))
   171  
   172  	if _, ok := attrs["-"]; ok {
   173  		return nil, errSkipField
   174  	}
   175  
   176  	digits := tags["digits"]
   177  	decimals := tags["decimals"]
   178  	size := tags["size"]
   179  	onDelete := tags["on_delete"]
   180  
   181  	initial.Clear()
   182  	if v, ok := tags["default"]; ok {
   183  		initial.Set(v)
   184  	}
   185  
   186  checkType:
   187  	switch f := addrField.Interface().(type) {
   188  	case Fielder:
   189  		fi.isFielder = true
   190  		if field.Kind() == reflect.Ptr {
   191  			err = fmt.Errorf("the model Fielder can not be use ptr")
   192  			goto end
   193  		}
   194  		fieldType = f.FieldType()
   195  		if fieldType&IsRelField > 0 {
   196  			err = fmt.Errorf("unsupport type custom field, please refer to https://github.com/astaxie/beego/blob/master/orm/models_fields.go#L24-L42")
   197  			goto end
   198  		}
   199  	default:
   200  		tag = "rel"
   201  		tagValue = tags[tag]
   202  		if tagValue != "" {
   203  			switch tagValue {
   204  			case "fk":
   205  				fieldType = RelForeignKey
   206  				break checkType
   207  			case "one":
   208  				fieldType = RelOneToOne
   209  				break checkType
   210  			case "m2m":
   211  				fieldType = RelManyToMany
   212  				if tv := tags["rel_table"]; tv != "" {
   213  					fi.relTable = tv
   214  				} else if tv := tags["rel_through"]; tv != "" {
   215  					fi.relThrough = tv
   216  				}
   217  				break checkType
   218  			default:
   219  				err = fmt.Errorf("rel only allow these value: fk, one, m2m")
   220  				goto wrongTag
   221  			}
   222  		}
   223  		tag = "reverse"
   224  		tagValue = tags[tag]
   225  		if tagValue != "" {
   226  			switch tagValue {
   227  			case "one":
   228  				fieldType = RelReverseOne
   229  				break checkType
   230  			case "many":
   231  				fieldType = RelReverseMany
   232  				if tv := tags["rel_table"]; tv != "" {
   233  					fi.relTable = tv
   234  				} else if tv := tags["rel_through"]; tv != "" {
   235  					fi.relThrough = tv
   236  				}
   237  				break checkType
   238  			default:
   239  				err = fmt.Errorf("reverse only allow these value: one, many")
   240  				goto wrongTag
   241  			}
   242  		}
   243  
   244  		fieldType, err = getFieldType(addrField)
   245  		if err != nil {
   246  			goto end
   247  		}
   248  		if fieldType == TypeVarCharField {
   249  			switch tags["type"] {
   250  			case "char":
   251  				fieldType = TypeCharField
   252  			case "text":
   253  				fieldType = TypeTextField
   254  			case "json":
   255  				fieldType = TypeJSONField
   256  			case "jsonb":
   257  				fieldType = TypeJsonbField
   258  			}
   259  		}
   260  		if fieldType == TypeFloatField && (digits != "" || decimals != "") {
   261  			fieldType = TypeDecimalField
   262  		}
   263  		if fieldType == TypeDateTimeField && tags["type"] == "date" {
   264  			fieldType = TypeDateField
   265  		}
   266  		if fieldType == TypeTimeField && tags["type"] == "time" {
   267  			fieldType = TypeTimeField
   268  		}
   269  	}
   270  
   271  	// check the rel and reverse type
   272  	// rel should Ptr
   273  	// reverse should slice []*struct
   274  	switch fieldType {
   275  	case RelForeignKey, RelOneToOne, RelReverseOne:
   276  		if field.Kind() != reflect.Ptr {
   277  			err = fmt.Errorf("rel/reverse:one field must be *%s", field.Type().Name())
   278  			goto end
   279  		}
   280  	case RelManyToMany, RelReverseMany:
   281  		if field.Kind() != reflect.Slice {
   282  			err = fmt.Errorf("rel/reverse:many field must be slice")
   283  			goto end
   284  		} else {
   285  			if field.Type().Elem().Kind() != reflect.Ptr {
   286  				err = fmt.Errorf("rel/reverse:many slice must be []*%s", field.Type().Elem().Name())
   287  				goto end
   288  			}
   289  		}
   290  	}
   291  
   292  	if fieldType&IsFieldType == 0 {
   293  		err = fmt.Errorf("wrong field type")
   294  		goto end
   295  	}
   296  
   297  	fi.fieldType = fieldType
   298  	fi.name = sf.Name
   299  	fi.column = getColumnName(fieldType, addrField, sf, tags["column"])
   300  	fi.addrValue = addrField
   301  	fi.sf = sf
   302  	fi.fullName = mi.fullName + mName + "." + sf.Name
   303  
   304  	fi.description = tags["description"]
   305  	fi.null = attrs["null"]
   306  	fi.index = attrs["index"]
   307  	fi.auto = attrs["auto"]
   308  	fi.pk = attrs["pk"]
   309  	fi.unique = attrs["unique"]
   310  
   311  	// Mark object property if there is attribute "default" in the orm configuration
   312  	if _, ok := tags["default"]; ok {
   313  		fi.colDefault = true
   314  	}
   315  
   316  	switch fieldType {
   317  	case RelManyToMany, RelReverseMany, RelReverseOne:
   318  		fi.null = false
   319  		fi.index = false
   320  		fi.auto = false
   321  		fi.pk = false
   322  		fi.unique = false
   323  	default:
   324  		fi.dbcol = true
   325  	}
   326  
   327  	switch fieldType {
   328  	case RelForeignKey, RelOneToOne, RelManyToMany:
   329  		fi.rel = true
   330  		if fieldType == RelOneToOne {
   331  			fi.unique = true
   332  		}
   333  	case RelReverseMany, RelReverseOne:
   334  		fi.reverse = true
   335  	}
   336  
   337  	if fi.rel && fi.dbcol {
   338  		switch onDelete {
   339  		case odCascade, odDoNothing:
   340  		case odSetDefault:
   341  			if !initial.Exist() {
   342  				err = errors.New("on_delete: set_default need set field a default value")
   343  				goto end
   344  			}
   345  		case odSetNULL:
   346  			if !fi.null {
   347  				err = errors.New("on_delete: set_null need set field null")
   348  				goto end
   349  			}
   350  		default:
   351  			if onDelete == "" {
   352  				onDelete = odCascade
   353  			} else {
   354  				err = fmt.Errorf("on_delete value expected choice in `cascade,set_null,set_default,do_nothing`, unknown `%s`", onDelete)
   355  				goto end
   356  			}
   357  		}
   358  
   359  		fi.onDelete = onDelete
   360  	}
   361  
   362  	switch fieldType {
   363  	case TypeBooleanField:
   364  	case TypeVarCharField, TypeCharField, TypeJSONField, TypeJsonbField:
   365  		if size != "" {
   366  			v, e := StrTo(size).Int32()
   367  			if e != nil {
   368  				err = fmt.Errorf("wrong size value `%s`", size)
   369  			} else {
   370  				fi.size = int(v)
   371  			}
   372  		} else {
   373  			fi.size = 255
   374  			fi.toText = true
   375  		}
   376  	case TypeTextField:
   377  		fi.index = false
   378  		fi.unique = false
   379  	case TypeTimeField, TypeDateField, TypeDateTimeField:
   380  		if attrs["auto_now"] {
   381  			fi.autoNow = true
   382  		} else if attrs["auto_now_add"] {
   383  			fi.autoNowAdd = true
   384  		}
   385  	case TypeFloatField:
   386  	case TypeDecimalField:
   387  		d1 := digits
   388  		d2 := decimals
   389  		v1, er1 := StrTo(d1).Int8()
   390  		v2, er2 := StrTo(d2).Int8()
   391  		if er1 != nil || er2 != nil {
   392  			err = fmt.Errorf("wrong digits/decimals value %s/%s", d2, d1)
   393  			goto end
   394  		}
   395  		fi.digits = int(v1)
   396  		fi.decimals = int(v2)
   397  	default:
   398  		switch {
   399  		case fieldType&IsIntegerField > 0:
   400  		case fieldType&IsRelField > 0:
   401  		}
   402  	}
   403  
   404  	if fieldType&IsIntegerField == 0 {
   405  		if fi.auto {
   406  			err = fmt.Errorf("non-integer type cannot set auto")
   407  			goto end
   408  		}
   409  	}
   410  
   411  	if fi.auto || fi.pk {
   412  		if fi.auto {
   413  			switch addrField.Elem().Kind() {
   414  			case reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint32, reflect.Uint64:
   415  			default:
   416  				err = fmt.Errorf("auto primary key only support int, int32, int64, uint, uint32, uint64 but found `%s`", addrField.Elem().Kind())
   417  				goto end
   418  			}
   419  			fi.pk = true
   420  		}
   421  		fi.null = false
   422  		fi.index = false
   423  		fi.unique = false
   424  	}
   425  
   426  	if fi.unique {
   427  		fi.index = false
   428  	}
   429  
   430  	// can not set default for these type
   431  	if fi.auto || fi.pk || fi.unique || fieldType == TypeTimeField || fieldType == TypeDateField || fieldType == TypeDateTimeField {
   432  		initial.Clear()
   433  	}
   434  
   435  	if initial.Exist() {
   436  		v := initial
   437  		switch fieldType {
   438  		case TypeBooleanField:
   439  			_, err = v.Bool()
   440  		case TypeFloatField, TypeDecimalField:
   441  			_, err = v.Float64()
   442  		case TypeBitField:
   443  			_, err = v.Int8()
   444  		case TypeSmallIntegerField:
   445  			_, err = v.Int16()
   446  		case TypeIntegerField:
   447  			_, err = v.Int32()
   448  		case TypeBigIntegerField:
   449  			_, err = v.Int64()
   450  		case TypePositiveBitField:
   451  			_, err = v.Uint8()
   452  		case TypePositiveSmallIntegerField:
   453  			_, err = v.Uint16()
   454  		case TypePositiveIntegerField:
   455  			_, err = v.Uint32()
   456  		case TypePositiveBigIntegerField:
   457  			_, err = v.Uint64()
   458  		}
   459  		if err != nil {
   460  			tag, tagValue = "default", tags["default"]
   461  			goto wrongTag
   462  		}
   463  	}
   464  
   465  	fi.initial = initial
   466  end:
   467  	if err != nil {
   468  		return nil, err
   469  	}
   470  	return
   471  wrongTag:
   472  	return nil, fmt.Errorf("wrong tag format: `%s:\"%s\"`, %s", tag, tagValue, err)
   473  }