Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package datastore | 5 package datastore |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "fmt" | 8 "fmt" |
| 9 "io" | 9 "io" |
| 10 "io/ioutil" | 10 "io/ioutil" |
| 11 "os" | 11 "os" |
| 12 "path/filepath" | 12 "path/filepath" |
| 13 "reflect" | |
| 14 "runtime" | 13 "runtime" |
| 15 "strings" | 14 "strings" |
| 16 | 15 |
| 17 "github.com/luci/luci-go/common/errors" | |
| 18 | |
| 19 "gopkg.in/yaml.v2" | 16 "gopkg.in/yaml.v2" |
| 20 ) | 17 ) |
| 21 | 18 |
| 22 type datastoreImpl struct { | |
| 23 RawInterface | |
| 24 | |
| 25 aid string | |
| 26 ns string | |
| 27 } | |
| 28 | |
| 29 var _ Interface = (*datastoreImpl)(nil) | |
| 30 | |
| 31 func (d *datastoreImpl) KeyForObj(src interface{}) *Key { | |
| 32 ret, err := d.KeyForObjErr(src) | |
| 33 if err != nil { | |
| 34 panic(err) | |
| 35 } | |
| 36 return ret | |
| 37 } | |
| 38 | |
| 39 func (d *datastoreImpl) KeyForObjErr(src interface{}) (*Key, error) { | |
|
dnj
2016/09/01 15:25:40
These all moved into key.go.
| |
| 40 return newKeyObjErr(d.aid, d.ns, getMGS(src)) | |
| 41 } | |
| 42 | |
| 43 func (d *datastoreImpl) MakeKey(elems ...interface{}) *Key { | |
| 44 return MakeKey(d.aid, d.ns, elems...) | |
| 45 } | |
| 46 | |
| 47 func (d *datastoreImpl) NewKey(kind, stringID string, intID int64, parent *Key) *Key { | |
| 48 return NewKey(d.aid, d.ns, kind, stringID, intID, parent) | |
| 49 } | |
| 50 | |
| 51 func (d *datastoreImpl) NewIncompleteKeys(count int, kind string, parent *Key) ( keys []*Key) { | |
| 52 if count > 0 { | |
| 53 keys = make([]*Key, count) | |
| 54 for i := range keys { | |
| 55 keys[i] = d.NewKey(kind, "", 0, parent) | |
| 56 } | |
| 57 } | |
| 58 return | |
| 59 } | |
| 60 | |
| 61 func (d *datastoreImpl) NewKeyToks(toks []KeyTok) *Key { | |
| 62 return NewKeyToks(d.aid, d.ns, toks) | |
| 63 } | |
| 64 | |
| 65 // PopulateKey loads key into obj. | |
| 66 // | |
| 67 // obj is any object that Interface.Get is able to accept. | |
| 68 // | |
| 69 // Upon successful application, this method will return true. If the key could | |
| 70 // not be applied to the object, this method will return false. It will panic if | |
| 71 // obj is an invalid datastore model. | |
| 72 // | |
| 73 // This method will panic if obj is an invalid datastore model. If the key could | |
| 74 // not be applied to the object, nothing will happen. | |
| 75 func PopulateKey(obj interface{}, key *Key) bool { | |
|
dnj
2016/09/01 15:25:40
This all moved into "interface.go".
| |
| 76 return populateKeyMGS(getMGS(obj), key) | |
| 77 } | |
| 78 | |
| 79 func populateKeyMGS(mgs MetaGetterSetter, key *Key) bool { | |
| 80 if mgs.SetMeta("key", key) { | |
| 81 return true | |
| 82 } | |
| 83 | |
| 84 lst := key.LastTok() | |
| 85 if lst.StringID != "" { | |
| 86 if !mgs.SetMeta("id", lst.StringID) { | |
| 87 return false | |
| 88 } | |
| 89 } else { | |
| 90 if !mgs.SetMeta("id", lst.IntID) { | |
| 91 return false | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 mgs.SetMeta("kind", lst.Kind) | |
| 96 mgs.SetMeta("parent", key.Parent()) | |
| 97 return true | |
| 98 } | |
| 99 | |
| 100 func checkMultiSliceType(v interface{}) error { | |
| 101 if reflect.TypeOf(v).Kind() == reflect.Slice { | |
| 102 return nil | |
| 103 } | |
| 104 return fmt.Errorf("argument must be a slice, not %T", v) | |
| 105 | |
| 106 } | |
| 107 | |
| 108 func runParseCallback(cbIface interface{}) (isKey, hasErr, hasCursorCB bool, mat *multiArgType) { | |
| 109 badSig := func() { | |
| 110 panic(fmt.Errorf( | |
| 111 "cb does not match the required callback signature: `%T` != `func(TYPE, [CursorCB]) [error]`", | |
| 112 cbIface)) | |
| 113 } | |
| 114 | |
| 115 if cbIface == nil { | |
| 116 badSig() | |
| 117 } | |
| 118 | |
| 119 // TODO(riannucci): Profile and determine if any of this is causing a re al | |
| 120 // slowdown. Could potentially cache reflection stuff by cbTyp? | |
| 121 cbTyp := reflect.TypeOf(cbIface) | |
| 122 | |
| 123 if cbTyp.Kind() != reflect.Func { | |
| 124 badSig() | |
| 125 } | |
| 126 | |
| 127 numIn := cbTyp.NumIn() | |
| 128 if numIn != 1 && numIn != 2 { | |
| 129 badSig() | |
| 130 } | |
| 131 | |
| 132 firstArg := cbTyp.In(0) | |
| 133 if firstArg == typeOfKey { | |
| 134 isKey = true | |
| 135 } else { | |
| 136 mat = mustParseArg(firstArg, false) | |
| 137 if mat.newElem == nil { | |
| 138 badSig() | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 hasCursorCB = numIn == 2 | |
| 143 if hasCursorCB && cbTyp.In(1) != typeOfCursorCB { | |
| 144 badSig() | |
| 145 } | |
| 146 | |
| 147 if cbTyp.NumOut() > 1 { | |
| 148 badSig() | |
| 149 } else if cbTyp.NumOut() == 1 && cbTyp.Out(0) != typeOfError { | |
| 150 badSig() | |
| 151 } | |
| 152 hasErr = cbTyp.NumOut() == 1 | |
| 153 | |
| 154 return | |
| 155 } | |
| 156 | |
| 157 func (d *datastoreImpl) AllocateIDs(ent ...interface{}) error { | |
| 158 if len(ent) == 0 { | |
| 159 return nil | |
| 160 } | |
| 161 | |
| 162 mma, err := makeMetaMultiArg(ent, mmaWriteKeys) | |
| 163 if err != nil { | |
| 164 panic(err) | |
| 165 } | |
| 166 | |
| 167 keys, _, err := mma.getKeysPMs(d.aid, d.ns, false) | |
| 168 if err != nil { | |
| 169 return err | |
| 170 } | |
| 171 if len(keys) == 0 { | |
| 172 return nil | |
| 173 } | |
| 174 | |
| 175 // Convert each key to be partial valid, assigning an integer ID of 0. C onfirm | |
| 176 // that each object can be populated with such a key. | |
| 177 for i, key := range keys { | |
| 178 keys[i] = key.Incomplete() | |
| 179 } | |
| 180 | |
| 181 var et errorTracker | |
| 182 it := mma.iterator(et.init(mma)) | |
| 183 err = filterStop(d.RawInterface.AllocateIDs(keys, func(key *Key, err err or) error { | |
| 184 it.next(func(mat *multiArgType, v reflect.Value) error { | |
| 185 if err != nil { | |
| 186 return err | |
| 187 } | |
| 188 | |
| 189 if !mat.setKey(v, key) { | |
| 190 return ErrInvalidKey | |
| 191 } | |
| 192 return nil | |
| 193 }) | |
| 194 | |
| 195 return nil | |
| 196 })) | |
| 197 if err == nil { | |
| 198 err = et.error() | |
| 199 | |
| 200 if err != nil && len(ent) == 1 { | |
| 201 // Single-argument Exists will return a single error. | |
| 202 err = errors.SingleError(err) | |
| 203 } | |
| 204 } | |
| 205 return err | |
| 206 } | |
| 207 | |
| 208 func (d *datastoreImpl) Run(q *Query, cbIface interface{}) error { | |
| 209 isKey, hasErr, hasCursorCB, mat := runParseCallback(cbIface) | |
| 210 | |
| 211 if isKey { | |
| 212 q = q.KeysOnly(true) | |
| 213 } | |
| 214 fq, err := q.Finalize() | |
| 215 if err != nil { | |
| 216 return err | |
| 217 } | |
| 218 | |
| 219 cbVal := reflect.ValueOf(cbIface) | |
| 220 var cb func(reflect.Value, CursorCB) error | |
| 221 switch { | |
| 222 case hasErr && hasCursorCB: | |
| 223 cb = func(v reflect.Value, cb CursorCB) error { | |
| 224 err := cbVal.Call([]reflect.Value{v, reflect.ValueOf(cb) })[0].Interface() | |
| 225 if err != nil { | |
| 226 return err.(error) | |
| 227 } | |
| 228 return nil | |
| 229 } | |
| 230 | |
| 231 case hasErr && !hasCursorCB: | |
| 232 cb = func(v reflect.Value, _ CursorCB) error { | |
| 233 err := cbVal.Call([]reflect.Value{v})[0].Interface() | |
| 234 if err != nil { | |
| 235 return err.(error) | |
| 236 } | |
| 237 return nil | |
| 238 } | |
| 239 | |
| 240 case !hasErr && hasCursorCB: | |
| 241 cb = func(v reflect.Value, cb CursorCB) error { | |
| 242 cbVal.Call([]reflect.Value{v, reflect.ValueOf(cb)}) | |
| 243 return nil | |
| 244 } | |
| 245 | |
| 246 case !hasErr && !hasCursorCB: | |
| 247 cb = func(v reflect.Value, _ CursorCB) error { | |
| 248 cbVal.Call([]reflect.Value{v}) | |
| 249 return nil | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 if isKey { | |
| 254 err = d.RawInterface.Run(fq, func(k *Key, _ PropertyMap, gc Curs orCB) error { | |
| 255 return cb(reflect.ValueOf(k), gc) | |
| 256 }) | |
| 257 } else { | |
| 258 err = d.RawInterface.Run(fq, func(k *Key, pm PropertyMap, gc Cur sorCB) error { | |
| 259 itm := mat.newElem() | |
| 260 if err := mat.setPM(itm, pm); err != nil { | |
| 261 return err | |
| 262 } | |
| 263 mat.setKey(itm, k) | |
| 264 return cb(itm, gc) | |
| 265 }) | |
| 266 } | |
| 267 return filterStop(err) | |
| 268 } | |
| 269 | |
| 270 func (d *datastoreImpl) Count(q *Query) (int64, error) { | |
| 271 fq, err := q.Finalize() | |
| 272 if err != nil { | |
| 273 return 0, err | |
| 274 } | |
| 275 v, err := d.RawInterface.Count(fq) | |
| 276 return v, filterStop(err) | |
| 277 } | |
| 278 | |
| 279 func (d *datastoreImpl) GetAll(q *Query, dst interface{}) error { | |
| 280 v := reflect.ValueOf(dst) | |
| 281 if v.Kind() != reflect.Ptr { | |
| 282 panic(fmt.Errorf("invalid GetAll dst: must have a ptr-to-slice: %T", dst)) | |
| 283 } | |
| 284 if !v.IsValid() || v.IsNil() { | |
| 285 panic(errors.New("invalid GetAll dst: <nil>")) | |
| 286 } | |
| 287 | |
| 288 if keys, ok := dst.(*[]*Key); ok { | |
| 289 fq, err := q.KeysOnly(true).Finalize() | |
| 290 if err != nil { | |
| 291 return err | |
| 292 } | |
| 293 | |
| 294 return d.RawInterface.Run(fq, func(k *Key, _ PropertyMap, _ Curs orCB) error { | |
| 295 *keys = append(*keys, k) | |
| 296 return nil | |
| 297 }) | |
| 298 } | |
| 299 fq, err := q.Finalize() | |
| 300 if err != nil { | |
| 301 return err | |
| 302 } | |
| 303 | |
| 304 slice := v.Elem() | |
| 305 mat := mustParseMultiArg(slice.Type()) | |
| 306 if mat.newElem == nil { | |
| 307 panic(fmt.Errorf("invalid GetAll dst (non-concrete element type) : %T", dst)) | |
| 308 } | |
| 309 | |
| 310 errs := map[int]error{} | |
| 311 i := 0 | |
| 312 err = filterStop(d.RawInterface.Run(fq, func(k *Key, pm PropertyMap, _ C ursorCB) error { | |
| 313 slice.Set(reflect.Append(slice, mat.newElem())) | |
| 314 itm := slice.Index(i) | |
| 315 mat.setKey(itm, k) | |
| 316 err := mat.setPM(itm, pm) | |
| 317 if err != nil { | |
| 318 errs[i] = err | |
| 319 } | |
| 320 i++ | |
| 321 return nil | |
| 322 })) | |
| 323 if err == nil { | |
| 324 if len(errs) > 0 { | |
| 325 me := make(errors.MultiError, slice.Len()) | |
| 326 for i, e := range errs { | |
| 327 me[i] = e | |
| 328 } | |
| 329 err = me | |
| 330 } | |
| 331 } | |
| 332 return err | |
| 333 } | |
| 334 | |
| 335 func (d *datastoreImpl) Exists(ent ...interface{}) (*ExistsResult, error) { | |
| 336 if len(ent) == 0 { | |
| 337 return nil, nil | |
| 338 } | |
| 339 | |
| 340 mma, err := makeMetaMultiArg(ent, mmaKeysOnly) | |
| 341 if err != nil { | |
| 342 panic(err) | |
| 343 } | |
| 344 | |
| 345 keys, _, err := mma.getKeysPMs(d.aid, d.ns, false) | |
| 346 if err != nil { | |
| 347 return nil, err | |
| 348 } | |
| 349 if len(keys) == 0 { | |
| 350 return nil, nil | |
| 351 } | |
| 352 | |
| 353 var bt boolTracker | |
| 354 it := mma.iterator(bt.init(mma)) | |
| 355 err = filterStop(d.RawInterface.GetMulti(keys, nil, func(_ PropertyMap, err error) error { | |
| 356 it.next(func(*multiArgType, reflect.Value) error { | |
| 357 return err | |
| 358 }) | |
| 359 return nil | |
| 360 })) | |
| 361 if err == nil { | |
| 362 err = bt.error() | |
| 363 | |
| 364 if err != nil && len(ent) == 1 { | |
| 365 // Single-argument Exists will return a single error. | |
| 366 err = errors.SingleError(err) | |
| 367 } | |
| 368 } | |
| 369 return bt.result(), err | |
| 370 } | |
| 371 | |
| 372 func (d *datastoreImpl) ExistsMulti(keys []*Key) (BoolList, error) { | |
| 373 v, err := d.Exists(keys) | |
| 374 if err != nil { | |
| 375 return nil, err | |
| 376 } | |
| 377 return v.List(0), nil | |
| 378 } | |
| 379 | |
| 380 func (d *datastoreImpl) Get(dst ...interface{}) (err error) { | |
| 381 if len(dst) == 0 { | |
| 382 return nil | |
| 383 } | |
| 384 | |
| 385 mma, err := makeMetaMultiArg(dst, mmaReadWrite) | |
| 386 if err != nil { | |
| 387 panic(err) | |
| 388 } | |
| 389 | |
| 390 keys, pms, err := mma.getKeysPMs(d.aid, d.ns, true) | |
| 391 if err != nil { | |
| 392 return err | |
| 393 } | |
| 394 if len(keys) == 0 { | |
| 395 return nil | |
| 396 } | |
| 397 | |
| 398 var et errorTracker | |
| 399 it := mma.iterator(et.init(mma)) | |
| 400 meta := NewMultiMetaGetter(pms) | |
| 401 err = filterStop(d.RawInterface.GetMulti(keys, meta, func(pm PropertyMap , err error) error { | |
| 402 it.next(func(mat *multiArgType, slot reflect.Value) error { | |
| 403 if err != nil { | |
| 404 return err | |
| 405 } | |
| 406 return mat.setPM(slot, pm) | |
| 407 }) | |
| 408 return nil | |
| 409 })) | |
| 410 | |
| 411 if err == nil { | |
| 412 err = et.error() | |
| 413 | |
| 414 if err != nil && len(dst) == 1 { | |
| 415 // Single-argument Get will return a single error. | |
| 416 err = errors.SingleError(err) | |
| 417 } | |
| 418 } | |
| 419 return err | |
| 420 } | |
| 421 | |
| 422 func (d *datastoreImpl) GetMulti(dst interface{}) error { | |
| 423 if err := checkMultiSliceType(dst); err != nil { | |
| 424 panic(err) | |
| 425 } | |
| 426 return d.Get(dst) | |
| 427 } | |
| 428 | |
| 429 func (d *datastoreImpl) Put(src ...interface{}) (err error) { | |
| 430 if len(src) == 0 { | |
| 431 return nil | |
| 432 } | |
| 433 | |
| 434 mma, err := makeMetaMultiArg(src, mmaReadWrite) | |
| 435 if err != nil { | |
| 436 panic(err) | |
| 437 } | |
| 438 | |
| 439 keys, vals, err := mma.getKeysPMs(d.aid, d.ns, false) | |
| 440 if err != nil { | |
| 441 return err | |
| 442 } | |
| 443 if len(keys) == 0 { | |
| 444 return nil | |
| 445 } | |
| 446 | |
| 447 i := 0 | |
| 448 var et errorTracker | |
| 449 it := mma.iterator(et.init(mma)) | |
| 450 err = filterStop(d.RawInterface.PutMulti(keys, vals, func(key *Key, err error) error { | |
| 451 it.next(func(mat *multiArgType, slot reflect.Value) error { | |
| 452 if err != nil { | |
| 453 return err | |
| 454 } | |
| 455 if key != keys[i] { | |
| 456 mat.setKey(slot, key) | |
| 457 } | |
| 458 return nil | |
| 459 }) | |
| 460 | |
| 461 i++ | |
| 462 return nil | |
| 463 })) | |
| 464 | |
| 465 if err == nil { | |
| 466 err = et.error() | |
| 467 | |
| 468 if err != nil && len(src) == 1 { | |
| 469 // Single-argument Put will return a single error. | |
| 470 err = errors.SingleError(err) | |
| 471 } | |
| 472 } | |
| 473 return err | |
| 474 } | |
| 475 | |
| 476 func (d *datastoreImpl) PutMulti(src interface{}) error { | |
| 477 if err := checkMultiSliceType(src); err != nil { | |
| 478 panic(err) | |
| 479 } | |
| 480 return d.Put(src) | |
| 481 } | |
| 482 | |
| 483 func (d *datastoreImpl) Delete(ent ...interface{}) error { | |
| 484 if len(ent) == 0 { | |
| 485 return nil | |
| 486 } | |
| 487 | |
| 488 mma, err := makeMetaMultiArg(ent, mmaKeysOnly) | |
| 489 if err != nil { | |
| 490 panic(err) | |
| 491 } | |
| 492 | |
| 493 keys, _, err := mma.getKeysPMs(d.aid, d.ns, false) | |
| 494 if err != nil { | |
| 495 return err | |
| 496 } | |
| 497 if len(keys) == 0 { | |
| 498 return nil | |
| 499 } | |
| 500 | |
| 501 var et errorTracker | |
| 502 it := mma.iterator(et.init(mma)) | |
| 503 err = filterStop(d.RawInterface.DeleteMulti(keys, func(err error) error { | |
| 504 it.next(func(*multiArgType, reflect.Value) error { | |
| 505 return err | |
| 506 }) | |
| 507 | |
| 508 return nil | |
| 509 })) | |
| 510 if err == nil { | |
| 511 err = et.error() | |
| 512 | |
| 513 if err != nil && len(ent) == 1 { | |
| 514 // Single-argument Delete will return a single error. | |
| 515 err = errors.SingleError(err) | |
| 516 } | |
| 517 } | |
| 518 return err | |
| 519 } | |
| 520 | |
| 521 func (d *datastoreImpl) DeleteMulti(keys []*Key) error { | |
| 522 return d.Delete(keys) | |
| 523 } | |
| 524 | |
| 525 func (d *datastoreImpl) Raw() RawInterface { | |
| 526 return d.RawInterface | |
| 527 } | |
| 528 | |
| 529 // ParseIndexYAML parses the contents of a index YAML file into a list of | 19 // ParseIndexYAML parses the contents of a index YAML file into a list of |
| 530 // IndexDefinitions. | 20 // IndexDefinitions. |
| 531 func ParseIndexYAML(content io.Reader) ([]*IndexDefinition, error) { | 21 func ParseIndexYAML(content io.Reader) ([]*IndexDefinition, error) { |
| 532 serialized, err := ioutil.ReadAll(content) | 22 serialized, err := ioutil.ReadAll(content) |
| 533 if err != nil { | 23 if err != nil { |
| 534 return nil, err | 24 return nil, err |
| 535 } | 25 } |
| 536 | 26 |
| 537 var m map[string][]*IndexDefinition | 27 var m map[string][]*IndexDefinition |
| 538 if err := yaml.Unmarshal(serialized, &m); err != nil { | 28 if err := yaml.Unmarshal(serialized, &m); err != nil { |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 601 } | 91 } |
| 602 } | 92 } |
| 603 | 93 |
| 604 if isRoot(currentDir) { | 94 if isRoot(currentDir) { |
| 605 return nil, fmt.Errorf("datastore: failed to find index YAML file") | 95 return nil, fmt.Errorf("datastore: failed to find index YAML file") |
| 606 } | 96 } |
| 607 | 97 |
| 608 currentDir = filepath.Dir(currentDir) | 98 currentDir = filepath.Dir(currentDir) |
| 609 } | 99 } |
| 610 } | 100 } |
| 611 | |
| 612 func filterStop(err error) error { | |
| 613 if err == Stop { | |
| 614 err = nil | |
| 615 } | |
| 616 return err | |
| 617 } | |
| OLD | NEW |