github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/databases/orm/models_info_f.go (about)

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