modernc.org/ql@v1.4.7/introspection.go (about) 1 // Copyright 2014 The ql Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ql // import "modernc.org/ql" 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/ast" 11 "reflect" 12 "strings" 13 "sync" 14 ) 15 16 var ( 17 schemaCache = map[reflect.Type]*StructInfo{} 18 schemaMu sync.RWMutex 19 ) 20 21 // StructInfo describes a struct type. An instance of StructInfo obtained from 22 // StructSchema is shared and must not be mutated. That includes the values 23 // pointed to by the elements of Fields and Indices. 24 type StructInfo struct { 25 Fields []*StructField // Fields describe the considered fields of a struct type. 26 HasID bool // Whether the struct has a considered field named ID of type int64. 27 Indices []*StructIndex // Indices describe indices defined by the index or uindex ql tags. 28 IsPtr bool // Whether the StructInfo was derived from a pointer to a struct. 29 } 30 31 // StructIndex describes an index defined by the ql tag index or uindex. 32 type StructIndex struct { 33 ColumnName string // Name of the column the index is on. 34 Name string // Name of the index. 35 Unique bool // Whether the index is unique. 36 } 37 38 // StructField describes a considered field of a struct type. 39 type StructField struct { 40 Index int // Index is the index of the field for reflect.Value.Field. 41 IsID bool // Whether the field corresponds to record id(). 42 IsPtr bool // Whether the field is a pointer type. 43 MarshalType reflect.Type // The reflect.Type a field must be converted to when marshaling or nil when it is assignable directly. (Field->value) 44 Name string // Field name or value of the name tag (like in `ql:"name foo"`). 45 ReflectType reflect.Type // The reflect.Type of the field. 46 Tags map[string]string // QL tags of this field. (`ql:"a, b c, d"` -> {"a": "", "b": "c", "d": ""}) 47 Type Type // QL type of the field. 48 UnmarshalType reflect.Type // The reflect.Type a value must be converted to when unmarshaling or nil when it is assignable directly. (Field<-value) 49 ZeroPtr reflect.Value // The reflect.Zero value of the field if it's a pointer type. 50 } 51 52 func (s *StructField) check(v interface{}) error { 53 t := reflect.TypeOf(v) 54 if !s.ReflectType.AssignableTo(t) { 55 if !s.ReflectType.ConvertibleTo(t) { 56 return fmt.Errorf("type %s (%v) cannot be converted to %T", s.ReflectType.Name(), s.ReflectType.Kind(), t.Name()) 57 } 58 59 s.MarshalType = t 60 } 61 62 if !t.AssignableTo(s.ReflectType) { 63 if !t.ConvertibleTo(s.ReflectType) { 64 return fmt.Errorf("type %s (%v) cannot be converted to %T", t.Name(), t.Kind(), s.ReflectType.Name()) 65 } 66 67 s.UnmarshalType = s.ReflectType 68 } 69 return nil 70 } 71 72 func parseTag(s string) map[string]string { 73 m := map[string]string{} 74 for _, v := range strings.Split(s, ",") { 75 v = strings.TrimSpace(v) 76 switch n := strings.IndexRune(v, ' '); { 77 case n < 0: 78 m[v] = "" 79 default: 80 m[v[:n]] = v[n+1:] 81 } 82 } 83 return m 84 } 85 86 // StructSchema returns StructInfo for v which must be a struct instance or a 87 // pointer to a struct. The info is computed only once for every type. 88 // Subsequent calls to StructSchema for the same type return a cached 89 // StructInfo. 90 // 91 // Note: The returned StructSchema is shared and must be not mutated, including 92 // any other data structures it may point to. 93 func StructSchema(v interface{}) (*StructInfo, error) { 94 if v == nil { 95 return nil, fmt.Errorf("cannot derive schema for %T(%v)", v, v) 96 } 97 98 typ := reflect.TypeOf(v) 99 schemaMu.RLock() 100 if r, ok := schemaCache[typ]; ok { 101 schemaMu.RUnlock() 102 return r, nil 103 } 104 105 schemaMu.RUnlock() 106 var schemaPtr bool 107 t := typ 108 if t.Kind() == reflect.Ptr { 109 t = t.Elem() 110 schemaPtr = true 111 } 112 if k := t.Kind(); k != reflect.Struct { 113 return nil, fmt.Errorf("cannot derive schema for type %T (%v)", v, k) 114 } 115 116 r := &StructInfo{IsPtr: schemaPtr} 117 for i := 0; i < t.NumField(); i++ { 118 f := t.Field(i) 119 fn := f.Name 120 if !ast.IsExported(fn) { 121 continue 122 } 123 124 tags := parseTag(f.Tag.Get("ql")) 125 if _, ok := tags["-"]; ok { 126 continue 127 } 128 129 if s := tags["name"]; s != "" { 130 fn = s 131 } 132 133 if fn == "ID" && f.Type.Kind() == reflect.Int64 { 134 r.HasID = true 135 } 136 var ix, unique bool 137 var xn string 138 xfn := fn 139 if s := tags["index"]; s != "" { 140 if _, ok := tags["uindex"]; ok { 141 return nil, fmt.Errorf("both index and uindex in QL struct tag") 142 } 143 144 ix, xn = true, s 145 } else if s := tags["uindex"]; s != "" { 146 if _, ok := tags["index"]; ok { 147 return nil, fmt.Errorf("both index and uindex in QL struct tag") 148 } 149 150 ix, unique, xn = true, true, s 151 } 152 if ix { 153 if fn == "ID" && r.HasID { 154 xfn = "id()" 155 } 156 r.Indices = append(r.Indices, &StructIndex{Name: xn, ColumnName: xfn, Unique: unique}) 157 } 158 159 sf := &StructField{Index: i, Name: fn, Tags: tags, Type: Type(-1), ReflectType: f.Type} 160 fk := sf.ReflectType.Kind() 161 if fk == reflect.Ptr { 162 sf.IsPtr = true 163 sf.ZeroPtr = reflect.Zero(sf.ReflectType) 164 sf.ReflectType = sf.ReflectType.Elem() 165 fk = sf.ReflectType.Kind() 166 } 167 168 switch fk { 169 case reflect.Bool: 170 sf.Type = Bool 171 if err := sf.check(false); err != nil { 172 return nil, err 173 } 174 case reflect.Int, reflect.Uint: 175 return nil, fmt.Errorf("only integers of fixed size can be used to derive a schema: %v", fk) 176 case reflect.Int8: 177 sf.Type = Int8 178 if err := sf.check(int8(0)); err != nil { 179 return nil, err 180 } 181 case reflect.Int16: 182 if err := sf.check(int16(0)); err != nil { 183 return nil, err 184 } 185 sf.Type = Int16 186 case reflect.Int32: 187 if err := sf.check(int32(0)); err != nil { 188 return nil, err 189 } 190 sf.Type = Int32 191 case reflect.Int64: 192 if sf.ReflectType.Name() == "Duration" && sf.ReflectType.PkgPath() == "time" { 193 sf.Type = Duration 194 break 195 } 196 197 sf.Type = Int64 198 if err := sf.check(int64(0)); err != nil { 199 return nil, err 200 } 201 case reflect.Uint8: 202 sf.Type = Uint8 203 if err := sf.check(uint8(0)); err != nil { 204 return nil, err 205 } 206 case reflect.Uint16: 207 sf.Type = Uint16 208 if err := sf.check(uint16(0)); err != nil { 209 return nil, err 210 } 211 case reflect.Uint32: 212 sf.Type = Uint32 213 if err := sf.check(uint32(0)); err != nil { 214 return nil, err 215 } 216 case reflect.Uint64: 217 sf.Type = Uint64 218 if err := sf.check(uint64(0)); err != nil { 219 return nil, err 220 } 221 case reflect.Float32: 222 sf.Type = Float32 223 if err := sf.check(float32(0)); err != nil { 224 return nil, err 225 } 226 case reflect.Float64: 227 sf.Type = Float64 228 if err := sf.check(float64(0)); err != nil { 229 return nil, err 230 } 231 case reflect.Complex64: 232 sf.Type = Complex64 233 if err := sf.check(complex64(0)); err != nil { 234 return nil, err 235 } 236 case reflect.Complex128: 237 sf.Type = Complex128 238 if err := sf.check(complex128(0)); err != nil { 239 return nil, err 240 } 241 case reflect.Slice: 242 sf.Type = Blob 243 if err := sf.check([]byte(nil)); err != nil { 244 return nil, err 245 } 246 case reflect.Struct: 247 switch sf.ReflectType.PkgPath() { 248 case "math/big": 249 switch sf.ReflectType.Name() { 250 case "Int": 251 sf.Type = BigInt 252 case "Rat": 253 sf.Type = BigRat 254 } 255 case "time": 256 switch sf.ReflectType.Name() { 257 case "Time": 258 sf.Type = Time 259 } 260 } 261 case reflect.String: 262 sf.Type = String 263 if err := sf.check(""); err != nil { 264 return nil, err 265 } 266 } 267 268 if sf.Type < 0 { 269 return nil, fmt.Errorf("cannot derive schema for type %s (%v)", sf.ReflectType.Name(), fk) 270 } 271 272 sf.IsID = fn == "ID" && r.HasID 273 r.Fields = append(r.Fields, sf) 274 } 275 276 schemaMu.Lock() 277 schemaCache[typ] = r 278 if t != typ { 279 r2 := *r 280 r2.IsPtr = false 281 schemaCache[t] = &r2 282 } 283 schemaMu.Unlock() 284 return r, nil 285 } 286 287 // MustStructSchema is like StructSchema but panics on error. It simplifies 288 // safe initialization of global variables holding StructInfo. 289 // 290 // MustStructSchema is safe for concurrent use by multiple goroutines. 291 func MustStructSchema(v interface{}) *StructInfo { 292 s, err := StructSchema(v) 293 if err != nil { 294 panic(err) 295 } 296 297 return s 298 } 299 300 // SchemaOptions amend the result of Schema. 301 type SchemaOptions struct { 302 // Don't wrap the CREATE statement(s) in a transaction. 303 NoTransaction bool 304 305 // Don't insert the IF NOT EXISTS clause in the CREATE statement(s). 306 NoIfNotExists bool 307 308 // Do not strip the "pkg." part from type name "pkg.Type", produce 309 // "pkg_Type" table name instead. Applies only when no name is passed 310 // to Schema(). 311 KeepPrefix bool 312 } 313 314 var zeroSchemaOptions SchemaOptions 315 316 // Schema returns a CREATE TABLE/INDEX statement(s) for a table derived from a 317 // struct or an error, if any. The table is named using the name parameter. If 318 // name is an empty string then the type name of the struct is used while non 319 // conforming characters are replaced by underscores. Value v can be also a 320 // pointer to a struct. 321 // 322 // Every considered struct field type must be one of the QL types or a type 323 // convertible to string, bool, int*, uint*, float* or complex* type or pointer 324 // to such type. Integers with a width dependent on the architecture can not be 325 // used. Only exported fields are considered. If an exported field QL tag 326 // contains "-" (`ql:"-"`) then such field is not considered. A field with name 327 // ID, having type int64, corresponds to id() - and is thus not a part of the 328 // CREATE statement. A field QL tag containing "index name" or "uindex name" 329 // triggers additionally creating an index or unique index on the respective 330 // field. Fields can be renamed using a QL tag "name newName". Fields are 331 // considered in the order of appearance. A QL tag is a struct tag part 332 // prefixed by "ql:". Tags can be combined, for example: 333 // 334 // type T struct { 335 // Foo string `ql:"index xFoo, name Bar"` 336 // } 337 // 338 // If opts.NoTransaction == true then the statement(s) are not wrapped in a 339 // transaction. If opt.NoIfNotExists == true then the CREATE statement(s) omits 340 // the IF NOT EXISTS clause. Passing nil opts is equal to passing 341 // &SchemaOptions{} 342 // 343 // Schema is safe for concurrent use by multiple goroutines. 344 func Schema(v interface{}, name string, opt *SchemaOptions) (List, error) { 345 if opt == nil { 346 opt = &zeroSchemaOptions 347 } 348 s, err := StructSchema(v) 349 if err != nil { 350 return List{}, err 351 } 352 353 var buf bytes.Buffer 354 if !opt.NoTransaction { 355 buf.WriteString("BEGIN TRANSACTION; ") 356 } 357 buf.WriteString("CREATE TABLE ") 358 if !opt.NoIfNotExists { 359 buf.WriteString("IF NOT EXISTS ") 360 } 361 if name == "" { 362 name = fmt.Sprintf("%T", v) 363 if !opt.KeepPrefix { 364 a := strings.Split(name, ".") 365 if l := len(a); l > 1 { 366 name = a[l-1] 367 } 368 } 369 nm := []rune{} 370 for _, v := range name { 371 switch { 372 case v >= '0' && v <= '9' || v == '_' || v >= 'a' && v <= 'z' || v >= 'A' && v <= 'Z': 373 // ok 374 default: 375 v = '_' 376 } 377 nm = append(nm, v) 378 } 379 name = string(nm) 380 } 381 buf.WriteString(name + " (") 382 for _, v := range s.Fields { 383 if v.IsID { 384 continue 385 } 386 387 buf.WriteString(fmt.Sprintf("%s %s, ", v.Name, v.Type)) 388 } 389 buf.WriteString("); ") 390 for _, v := range s.Indices { 391 buf.WriteString("CREATE ") 392 if v.Unique { 393 buf.WriteString("UNIQUE ") 394 } 395 buf.WriteString("INDEX ") 396 if !opt.NoIfNotExists { 397 buf.WriteString("IF NOT EXISTS ") 398 } 399 buf.WriteString(fmt.Sprintf("%s ON %s (%s); ", v.Name, name, v.ColumnName)) 400 } 401 if !opt.NoTransaction { 402 buf.WriteString("COMMIT; ") 403 } 404 l, err := Compile(buf.String()) 405 if err != nil { 406 return List{}, fmt.Errorf("%s: %v", buf.String(), err) 407 } 408 409 return l, nil 410 } 411 412 // MustSchema is like Schema but panics on error. It simplifies safe 413 // initialization of global variables holding compiled schemas. 414 // 415 // MustSchema is safe for concurrent use by multiple goroutines. 416 func MustSchema(v interface{}, name string, opt *SchemaOptions) List { 417 l, err := Schema(v, name, opt) 418 if err != nil { 419 panic(err) 420 } 421 422 return l 423 } 424 425 // Marshal converts, in the order of appearance, fields of a struct instance v 426 // to []interface{} or an error, if any. Value v can be also a pointer to a 427 // struct. 428 // 429 // Every considered struct field type must be one of the QL types or a type 430 // convertible to string, bool, int*, uint*, float* or complex* type or pointer 431 // to such type. Integers with a width dependent on the architecture can not be 432 // used. Only exported fields are considered. If an exported field QL tag 433 // contains "-" then such field is not considered. A QL tag is a struct tag 434 // part prefixed by "ql:". Field with name ID, having type int64, corresponds 435 // to id() - and is thus not part of the result. 436 // 437 // Marshal is safe for concurrent use by multiple goroutines. 438 func Marshal(v interface{}) ([]interface{}, error) { 439 s, err := StructSchema(v) 440 if err != nil { 441 return nil, err 442 } 443 444 val := reflect.ValueOf(v) 445 if s.IsPtr { 446 val = val.Elem() 447 } 448 n := len(s.Fields) 449 if s.HasID { 450 n-- 451 } 452 r := make([]interface{}, n) 453 j := 0 454 for _, v := range s.Fields { 455 if v.IsID { 456 continue 457 } 458 459 f := val.Field(v.Index) 460 if v.IsPtr { 461 if f.IsNil() { 462 r[j] = nil 463 j++ 464 continue 465 } 466 467 f = f.Elem() 468 } 469 if m := v.MarshalType; m != nil { 470 f = f.Convert(m) 471 } 472 r[j] = f.Interface() 473 j++ 474 } 475 return r, nil 476 } 477 478 // MustMarshal is like Marshal but panics on error. It simplifies marshaling of 479 // "safe" types, like eg. those which were already verified by Schema or 480 // MustSchema. When the underlying Marshal returns an error, MustMarshal 481 // panics. 482 // 483 // MustMarshal is safe for concurrent use by multiple goroutines. 484 func MustMarshal(v interface{}) []interface{} { 485 r, err := Marshal(v) 486 if err != nil { 487 panic(err) 488 } 489 490 return r 491 } 492 493 // Unmarshal stores data from []interface{} in the struct value pointed to by 494 // v. 495 // 496 // Every considered struct field type must be one of the QL types or a type 497 // convertible to string, bool, int*, uint*, float* or complex* type or pointer 498 // to such type. Integers with a width dependent on the architecture can not be 499 // used. Only exported fields are considered. If an exported field QL tag 500 // contains "-" then such field is not considered. A QL tag is a struct tag 501 // part prefixed by "ql:". Fields are considered in the order of appearance. 502 // Types of values in data must be compatible with the corresponding considered 503 // field of v. 504 // 505 // If the struct has no ID field then the number of values in data must be equal 506 // to the number of considered fields of v. 507 // 508 // type T struct { 509 // A bool 510 // B string 511 // } 512 // 513 // Assuming the schema is 514 // 515 // CREATE TABLE T (A bool, B string); 516 // 517 // Data might be a result of queries like 518 // 519 // SELECT * FROM T; 520 // SELECT A, B FROM T; 521 // 522 // If the struct has a considered ID field then the number of values in data 523 // must be equal to the number of considered fields in v - or one less. In the 524 // later case the ID field is not set. 525 // 526 // type U struct { 527 // ID int64 528 // A bool 529 // B string 530 // } 531 // 532 // Assuming the schema is 533 // 534 // CREATE TABLE T (A bool, B string); 535 // 536 // Data might be a result of queries like 537 // 538 // SELECT * FROM T; // ID not set 539 // SELECT A, B FROM T; // ID not set 540 // SELECT id(), A, B FROM T; // ID is set 541 // 542 // To unmarshal a value from data into a pointer field of v, Unmarshal first 543 // handles the case of the value being nil. In that case, Unmarshal sets the 544 // pointer to nil. Otherwise, Unmarshal unmarshals the data value into value 545 // pointed at by the pointer. If the pointer is nil, Unmarshal allocates a new 546 // value for it to point to. 547 // 548 // Unmarshal is safe for concurrent use by multiple goroutines. 549 func Unmarshal(v interface{}, data []interface{}) (err error) { 550 defer func() { 551 if r := recover(); r != nil { 552 var ok bool 553 if err, ok = r.(error); !ok { 554 err = fmt.Errorf("%v", r) 555 } 556 err = fmt.Errorf("unmarshal: %v", err) 557 } 558 }() 559 560 s, err := StructSchema(v) 561 if err != nil { 562 return err 563 } 564 565 if !s.IsPtr { 566 return fmt.Errorf("unmarshal: need a pointer to a struct") 567 } 568 569 id := false 570 nv, nf := len(data), len(s.Fields) 571 switch s.HasID { 572 case true: 573 switch { 574 case nv == nf: 575 id = true 576 case nv == nf-1: 577 // ok 578 default: 579 return fmt.Errorf("unmarshal: got %d values, need %d or %d", nv, nf-1, nf) 580 } 581 default: 582 switch { 583 case nv == nf: 584 // ok 585 default: 586 return fmt.Errorf("unmarshal: got %d values, need %d", nv, nf) 587 } 588 } 589 590 j := 0 591 vVal := reflect.ValueOf(v) 592 if s.IsPtr { 593 vVal = vVal.Elem() 594 } 595 for _, sf := range s.Fields { 596 if sf.IsID && !id { 597 continue 598 } 599 600 d := data[j] 601 val := reflect.ValueOf(d) 602 j++ 603 604 fVal := vVal.Field(sf.Index) 605 if u := sf.UnmarshalType; u != nil { 606 val = val.Convert(u) 607 } 608 if !sf.IsPtr { 609 fVal.Set(val) 610 continue 611 } 612 613 if d == nil { 614 fVal.Set(sf.ZeroPtr) 615 continue 616 } 617 618 if fVal.IsNil() { 619 fVal.Set(reflect.New(sf.ReflectType)) 620 } 621 622 fVal.Elem().Set(val) 623 } 624 return nil 625 }