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 }