123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- // 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 (
- "errors"
- "fmt"
- "reflect"
- "strings"
- )
- var errSkipField = errors.New("skip field")
- // field info collection
- type fields struct {
- pk *fieldInfo
- columns map[string]*fieldInfo
- fields map[string]*fieldInfo
- fieldsLow map[string]*fieldInfo
- fieldsByType map[int][]*fieldInfo
- fieldsRel []*fieldInfo
- fieldsReverse []*fieldInfo
- fieldsDB []*fieldInfo
- rels []*fieldInfo
- orders []string
- dbcols []string
- }
- // add field info
- func (f *fields) Add(fi *fieldInfo) (added bool) {
- if f.fields[fi.name] == nil && f.columns[fi.column] == nil {
- f.columns[fi.column] = fi
- f.fields[fi.name] = fi
- f.fieldsLow[strings.ToLower(fi.name)] = fi
- } else {
- return
- }
- if _, ok := f.fieldsByType[fi.fieldType]; ok == false {
- f.fieldsByType[fi.fieldType] = make([]*fieldInfo, 0)
- }
- f.fieldsByType[fi.fieldType] = append(f.fieldsByType[fi.fieldType], fi)
- f.orders = append(f.orders, fi.column)
- if fi.dbcol {
- f.dbcols = append(f.dbcols, fi.column)
- f.fieldsDB = append(f.fieldsDB, fi)
- }
- if fi.rel {
- f.fieldsRel = append(f.fieldsRel, fi)
- }
- if fi.reverse {
- f.fieldsReverse = append(f.fieldsReverse, fi)
- }
- return true
- }
- // get field info by name
- func (f *fields) GetByName(name string) *fieldInfo {
- return f.fields[name]
- }
- // get field info by column name
- func (f *fields) GetByColumn(column string) *fieldInfo {
- return f.columns[column]
- }
- // get field info by string, name is prior
- func (f *fields) GetByAny(name string) (*fieldInfo, bool) {
- if fi, ok := f.fields[name]; ok {
- return fi, ok
- }
- if fi, ok := f.fieldsLow[strings.ToLower(name)]; ok {
- return fi, ok
- }
- if fi, ok := f.columns[name]; ok {
- return fi, ok
- }
- return nil, false
- }
- // create new field info collection
- func newFields() *fields {
- f := new(fields)
- f.fields = make(map[string]*fieldInfo)
- f.fieldsLow = make(map[string]*fieldInfo)
- f.columns = make(map[string]*fieldInfo)
- f.fieldsByType = make(map[int][]*fieldInfo)
- return f
- }
- // single field info
- type fieldInfo struct {
- mi *modelInfo
- fieldIndex []int
- fieldType int
- dbcol bool // table column fk and onetoone
- inModel bool
- name string
- fullName string
- column string
- addrValue reflect.Value
- sf reflect.StructField
- auto bool
- pk bool
- null bool
- index bool
- unique bool
- colDefault bool // whether has default tag
- initial StrTo // store the default value
- size int
- toText bool
- autoNow bool
- autoNowAdd bool
- rel bool // if type equal to RelForeignKey, RelOneToOne, RelManyToMany then true
- reverse bool
- reverseField string
- reverseFieldInfo *fieldInfo
- reverseFieldInfoTwo *fieldInfo
- reverseFieldInfoM2M *fieldInfo
- relTable string
- relThrough string
- relThroughModelInfo *modelInfo
- relModelInfo *modelInfo
- digits int
- decimals int
- isFielder bool // implement Fielder interface
- onDelete string
- }
- // new field info
- func newFieldInfo(mi *modelInfo, field reflect.Value, sf reflect.StructField, mName string) (fi *fieldInfo, err error) {
- var (
- tag string
- tagValue string
- initial StrTo // store the default value
- fieldType int
- attrs map[string]bool
- tags map[string]string
- addrField reflect.Value
- )
- fi = new(fieldInfo)
- // if field which CanAddr is the follow type
- // A value is addressable if it is an element of a slice,
- // an element of an addressable array, a field of an
- // addressable struct, or the result of dereferencing a pointer.
- addrField = field
- if field.CanAddr() && field.Kind() != reflect.Ptr {
- addrField = field.Addr()
- if _, ok := addrField.Interface().(Fielder); !ok {
- if field.Kind() == reflect.Slice {
- addrField = field
- }
- }
- }
- attrs, tags = parseStructTag(sf.Tag.Get(defaultStructTagName))
- if _, ok := attrs["-"]; ok {
- return nil, errSkipField
- }
- digits := tags["digits"]
- decimals := tags["decimals"]
- size := tags["size"]
- onDelete := tags["on_delete"]
- initial.Clear()
- if v, ok := tags["default"]; ok {
- initial.Set(v)
- }
- checkType:
- switch f := addrField.Interface().(type) {
- case Fielder:
- fi.isFielder = true
- if field.Kind() == reflect.Ptr {
- err = fmt.Errorf("the model Fielder can not be use ptr")
- goto end
- }
- fieldType = f.FieldType()
- if fieldType&IsRelField > 0 {
- err = fmt.Errorf("unsupport type custom field, please refer to https://github.com/astaxie/beego/blob/master/orm/models_fields.go#L24-L42")
- goto end
- }
- default:
- tag = "rel"
- tagValue = tags[tag]
- if tagValue != "" {
- switch tagValue {
- case "fk":
- fieldType = RelForeignKey
- break checkType
- case "one":
- fieldType = RelOneToOne
- break checkType
- case "m2m":
- fieldType = RelManyToMany
- if tv := tags["rel_table"]; tv != "" {
- fi.relTable = tv
- } else if tv := tags["rel_through"]; tv != "" {
- fi.relThrough = tv
- }
- break checkType
- default:
- err = fmt.Errorf("rel only allow these value: fk, one, m2m")
- goto wrongTag
- }
- }
- tag = "reverse"
- tagValue = tags[tag]
- if tagValue != "" {
- switch tagValue {
- case "one":
- fieldType = RelReverseOne
- break checkType
- case "many":
- fieldType = RelReverseMany
- if tv := tags["rel_table"]; tv != "" {
- fi.relTable = tv
- } else if tv := tags["rel_through"]; tv != "" {
- fi.relThrough = tv
- }
- break checkType
- default:
- err = fmt.Errorf("reverse only allow these value: one, many")
- goto wrongTag
- }
- }
- fieldType, err = getFieldType(addrField)
- if err != nil {
- goto end
- }
- if fieldType == TypeCharField {
- switch tags["type"] {
- case "text":
- fieldType = TypeTextField
- case "json":
- fieldType = TypeJSONField
- case "jsonb":
- fieldType = TypeJsonbField
- }
- }
- if fieldType == TypeFloatField && (digits != "" || decimals != "") {
- fieldType = TypeDecimalField
- }
- if fieldType == TypeDateTimeField && tags["type"] == "date" {
- fieldType = TypeDateField
- }
- if fieldType == TypeTimeField && tags["type"] == "time" {
- fieldType = TypeTimeField
- }
- }
- // check the rel and reverse type
- // rel should Ptr
- // reverse should slice []*struct
- switch fieldType {
- case RelForeignKey, RelOneToOne, RelReverseOne:
- if field.Kind() != reflect.Ptr {
- err = fmt.Errorf("rel/reverse:one field must be *%s", field.Type().Name())
- goto end
- }
- case RelManyToMany, RelReverseMany:
- if field.Kind() != reflect.Slice {
- err = fmt.Errorf("rel/reverse:many field must be slice")
- goto end
- } else {
- if field.Type().Elem().Kind() != reflect.Ptr {
- err = fmt.Errorf("rel/reverse:many slice must be []*%s", field.Type().Elem().Name())
- goto end
- }
- }
- }
- if fieldType&IsFieldType == 0 {
- err = fmt.Errorf("wrong field type")
- goto end
- }
- fi.fieldType = fieldType
- fi.name = sf.Name
- fi.column = getColumnName(fieldType, addrField, sf, tags["column"])
- fi.addrValue = addrField
- fi.sf = sf
- fi.fullName = mi.fullName + mName + "." + sf.Name
- fi.null = attrs["null"]
- fi.index = attrs["index"]
- fi.auto = attrs["auto"]
- fi.pk = attrs["pk"]
- fi.unique = attrs["unique"]
- // Mark object property if there is attribute "default" in the orm configuration
- if _, ok := tags["default"]; ok {
- fi.colDefault = true
- }
- switch fieldType {
- case RelManyToMany, RelReverseMany, RelReverseOne:
- fi.null = false
- fi.index = false
- fi.auto = false
- fi.pk = false
- fi.unique = false
- default:
- fi.dbcol = true
- }
- switch fieldType {
- case RelForeignKey, RelOneToOne, RelManyToMany:
- fi.rel = true
- if fieldType == RelOneToOne {
- fi.unique = true
- }
- case RelReverseMany, RelReverseOne:
- fi.reverse = true
- }
- if fi.rel && fi.dbcol {
- switch onDelete {
- case odCascade, odDoNothing:
- case odSetDefault:
- if initial.Exist() == false {
- err = errors.New("on_delete: set_default need set field a default value")
- goto end
- }
- case odSetNULL:
- if fi.null == false {
- err = errors.New("on_delete: set_null need set field null")
- goto end
- }
- default:
- if onDelete == "" {
- onDelete = odCascade
- } else {
- err = fmt.Errorf("on_delete value expected choice in `cascade,set_null,set_default,do_nothing`, unknown `%s`", onDelete)
- goto end
- }
- }
- fi.onDelete = onDelete
- }
- switch fieldType {
- case TypeBooleanField:
- case TypeCharField, TypeJSONField, TypeJsonbField:
- if size != "" {
- v, e := StrTo(size).Int32()
- if e != nil {
- err = fmt.Errorf("wrong size value `%s`", size)
- } else {
- fi.size = int(v)
- }
- } else {
- fi.size = 255
- fi.toText = true
- }
- case TypeTextField:
- fi.index = false
- fi.unique = false
- case TypeTimeField, TypeDateField, TypeDateTimeField:
- if attrs["auto_now"] {
- fi.autoNow = true
- } else if attrs["auto_now_add"] {
- fi.autoNowAdd = true
- }
- case TypeFloatField:
- case TypeDecimalField:
- d1 := digits
- d2 := decimals
- v1, er1 := StrTo(d1).Int8()
- v2, er2 := StrTo(d2).Int8()
- if er1 != nil || er2 != nil {
- err = fmt.Errorf("wrong digits/decimals value %s/%s", d2, d1)
- goto end
- }
- fi.digits = int(v1)
- fi.decimals = int(v2)
- default:
- switch {
- case fieldType&IsIntegerField > 0:
- case fieldType&IsRelField > 0:
- }
- }
- if fieldType&IsIntegerField == 0 {
- if fi.auto {
- err = fmt.Errorf("non-integer type cannot set auto")
- goto end
- }
- }
- if fi.auto || fi.pk {
- if fi.auto {
- switch addrField.Elem().Kind() {
- case reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint32, reflect.Uint64:
- default:
- err = fmt.Errorf("auto primary key only support int, int32, int64, uint, uint32, uint64 but found `%s`", addrField.Elem().Kind())
- goto end
- }
- fi.pk = true
- }
- fi.null = false
- fi.index = false
- fi.unique = false
- }
- if fi.unique {
- fi.index = false
- }
- // can not set default for these type
- if fi.auto || fi.pk || fi.unique || fieldType == TypeTimeField || fieldType == TypeDateField || fieldType == TypeDateTimeField {
- initial.Clear()
- }
- if initial.Exist() {
- v := initial
- switch fieldType {
- case TypeBooleanField:
- _, err = v.Bool()
- case TypeFloatField, TypeDecimalField:
- _, err = v.Float64()
- case TypeBitField:
- _, err = v.Int8()
- case TypeSmallIntegerField:
- _, err = v.Int16()
- case TypeIntegerField:
- _, err = v.Int32()
- case TypeBigIntegerField:
- _, err = v.Int64()
- case TypePositiveBitField:
- _, err = v.Uint8()
- case TypePositiveSmallIntegerField:
- _, err = v.Uint16()
- case TypePositiveIntegerField:
- _, err = v.Uint32()
- case TypePositiveBigIntegerField:
- _, err = v.Uint64()
- }
- if err != nil {
- tag, tagValue = "default", tags["default"]
- goto wrongTag
- }
- }
- fi.initial = initial
- end:
- if err != nil {
- return nil, err
- }
- return
- wrongTag:
- return nil, fmt.Errorf("wrong tag format: `%s:\"%s\"`, %s", tag, tagValue, err)
- }
|