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 }