OLD | NEW |
1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 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 cloud | 5 package cloud |
6 | 6 |
7 import ( | 7 import ( |
8 "fmt" | 8 "fmt" |
9 "reflect" | 9 "reflect" |
10 "strings" | 10 "strings" |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
257 func (bds *boundDatastore) prepareNativeQuery(fq *ds.FinalizedQuery) *datastore.
Query { | 257 func (bds *boundDatastore) prepareNativeQuery(fq *ds.FinalizedQuery) *datastore.
Query { |
258 nq := datastore.NewQuery(fq.Kind()) | 258 nq := datastore.NewQuery(fq.Kind()) |
259 if bds.transaction != nil { | 259 if bds.transaction != nil { |
260 nq = nq.Transaction(bds.transaction) | 260 nq = nq.Transaction(bds.transaction) |
261 } | 261 } |
262 | 262 |
263 // nativeFilter translates a filter field. If the translation fails, we'
ll | 263 // nativeFilter translates a filter field. If the translation fails, we'
ll |
264 // pass the result through to the underlying datastore and allow it to | 264 // pass the result through to the underlying datastore and allow it to |
265 // reject it. | 265 // reject it. |
266 nativeFilter := func(prop ds.Property) interface{} { | 266 nativeFilter := func(prop ds.Property) interface{} { |
267 » » if np, err := bds.gaePropertyToNative("", []ds.Property{prop});
err == nil { | 267 » » if np, err := bds.gaePropertyToNative("", prop); err == nil { |
268 return np.Value | 268 return np.Value |
269 } | 269 } |
270 return prop.Value() | 270 return prop.Value() |
271 } | 271 } |
272 | 272 |
273 // Equality filters. | 273 // Equality filters. |
274 for field, props := range fq.EqFilters() { | 274 for field, props := range fq.EqFilters() { |
275 for _, prop := range props { | 275 for _, prop := range props { |
276 nq = nq.Filter(fmt.Sprintf("%s =", field), nativeFilter(
prop)) | 276 nq = nq.Filter(fmt.Sprintf("%s =", field), nativeFilter(
prop)) |
277 } | 277 } |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
326 nq = nq.Order(prop) | 326 nq = nq.Order(prop) |
327 } | 327 } |
328 | 328 |
329 return nq | 329 return nq |
330 } | 330 } |
331 | 331 |
332 func (bds *boundDatastore) mkNPLS(base ds.PropertyMap) *nativePropertyLoadSaver
{ | 332 func (bds *boundDatastore) mkNPLS(base ds.PropertyMap) *nativePropertyLoadSaver
{ |
333 return &nativePropertyLoadSaver{bds: bds, pmap: clonePropertyMap(base)} | 333 return &nativePropertyLoadSaver{bds: bds, pmap: clonePropertyMap(base)} |
334 } | 334 } |
335 | 335 |
336 func (bds *boundDatastore) gaePropertyToNative(name string, props []ds.Property)
(nativeProp datastore.Property, err error) { | 336 func (bds *boundDatastore) gaePropertyToNative(name string, pdata ds.PropertyDat
a) (nativeProp datastore.Property, err error) { |
337 nativeProp.Name = name | 337 nativeProp.Name = name |
338 | 338 |
339 » nativeValues := make([]interface{}, len(props)) | 339 » convert := func(prop *ds.Property) (interface{}, error) { |
340 » for i, prop := range props { | |
341 switch pt := prop.Type(); pt { | 340 switch pt := prop.Type(); pt { |
342 case ds.PTNull, ds.PTInt, ds.PTTime, ds.PTBool, ds.PTBytes, ds.P
TString, ds.PTFloat: | 341 case ds.PTNull, ds.PTInt, ds.PTTime, ds.PTBool, ds.PTBytes, ds.P
TString, ds.PTFloat: |
343 » » » nativeValues[i] = prop.Value() | 342 » » » return prop.Value(), nil |
344 » » » break | |
345 | 343 |
346 case ds.PTKey: | 344 case ds.PTKey: |
347 » » » nativeValues[i] = bds.gaeKeysToNative(prop.Value().(*ds.
Key))[0] | 345 » » » return bds.gaeKeysToNative(prop.Value().(*ds.Key))[0], n
il |
348 | 346 |
349 default: | 347 default: |
350 » » » err = fmt.Errorf("unsupported property type at %d: %v",
i, pt) | 348 » » » return nil, fmt.Errorf("unsupported property type: %v",
pt) |
351 » » » return | |
352 } | 349 } |
353 } | 350 } |
354 | 351 |
355 » if len(nativeValues) == 1 { | 352 » switch t := pdata.(type) { |
356 » » nativeProp.Value = nativeValues[0] | 353 » case ds.Property: |
357 » » nativeProp.NoIndex = (props[0].IndexSetting() != ds.ShouldIndex) | 354 » » if nativeProp.Value, err = convert(&t); err != nil { |
358 » } else { | 355 » » » return |
359 » » // We must always index list values. | 356 » » } |
360 » » nativeProp.Value = nativeValues | 357 » » nativeProp.NoIndex = (t.IndexSetting() != ds.ShouldIndex) |
| 358 |
| 359 » case ds.PropertySlice: |
| 360 » » // Don't index by default. If *any* sub-property requests being
indexed, |
| 361 » » // then we will index. |
| 362 » » nativeProp.NoIndex = true |
| 363 |
| 364 » » // Pack this into an interface{} so it is marked as a multi-valu
e. |
| 365 » » multiProp := make([]interface{}, len(t)) |
| 366 » » for i := range t { |
| 367 » » » prop := &t[i] |
| 368 » » » if multiProp[i], err = convert(prop); err != nil { |
| 369 » » » » return |
| 370 » » » } |
| 371 |
| 372 » » » if prop.IndexSetting() == ds.ShouldIndex { |
| 373 » » » » nativeProp.NoIndex = false |
| 374 » » » } |
| 375 » » } |
| 376 » » nativeProp.Value = multiProp |
| 377 |
| 378 » default: |
| 379 » » err = fmt.Errorf("unsupported PropertyData type for %q: %T", nam
e, pdata) |
361 } | 380 } |
| 381 |
362 return | 382 return |
363 } | 383 } |
364 | 384 |
365 func (bds *boundDatastore) nativePropertyToGAE(nativeProp datastore.Property) (n
ame string, props []ds.Property, err error) { | 385 func (bds *boundDatastore) nativePropertyToGAE(nativeProp datastore.Property) (n
ame string, pdata ds.PropertyData, err error) { |
366 name = nativeProp.Name | 386 name = nativeProp.Name |
367 | 387 |
368 » var nativeValues []interface{} | 388 » convert := func(nv interface{}, prop *ds.Property) error { |
369 » // Slice of supported native type. Break this into a slice of datastore | 389 » » switch nvt := nv.(type) { |
370 » // properties. | 390 » » case nil: |
371 » // | 391 » » » nv = nil |
372 » // It must be an []interface{}. | |
373 » if rv := reflect.ValueOf(nativeProp.Value); rv.Kind() == reflect.Slice &
& rv.Type().Elem().Kind() == reflect.Interface { | |
374 » » nativeValues = rv.Interface().([]interface{}) | |
375 » } else { | |
376 » » nativeValues = []interface{}{nativeProp.Value} | |
377 » } | |
378 | 392 |
379 if len(nativeValues) == 0 { | |
380 return | |
381 } | |
382 | |
383 props = make([]ds.Property, len(nativeValues)) | |
384 for i, nv := range nativeValues { | |
385 switch nvt := nv.(type) { | |
386 case int64, bool, string, float64, []byte: | 393 case int64, bool, string, float64, []byte: |
387 break | 394 break |
388 | 395 |
389 case time.Time: | 396 case time.Time: |
390 // Cloud datastore library returns local time. | 397 // Cloud datastore library returns local time. |
391 nv = nvt.UTC() | 398 nv = nvt.UTC() |
392 | 399 |
393 case *datastore.Key: | 400 case *datastore.Key: |
394 nv = bds.nativeKeysToGAE(nvt)[0] | 401 nv = bds.nativeKeysToGAE(nvt)[0] |
395 | 402 |
396 default: | 403 default: |
397 » » » err = fmt.Errorf("element %d has unsupported datastore.V
alue type %T", i, nv) | 404 » » » return fmt.Errorf("unsupported datastore.Value type for
%q: %T", name, nvt) |
398 » » » return | |
399 } | 405 } |
400 | 406 |
401 indexSetting := ds.ShouldIndex | 407 indexSetting := ds.ShouldIndex |
402 if nativeProp.NoIndex { | 408 if nativeProp.NoIndex { |
403 indexSetting = ds.NoIndex | 409 indexSetting = ds.NoIndex |
404 } | 410 } |
405 » » props[i].SetValue(nv, indexSetting) | 411 » » prop.SetValue(nv, indexSetting) |
| 412 » » return nil |
406 } | 413 } |
| 414 |
| 415 // Slice of supported native type. Break this into a slice of datastore |
| 416 // properties. |
| 417 // |
| 418 // It must be an []interface{}. |
| 419 if rv := reflect.ValueOf(nativeProp.Value); rv.Kind() == reflect.Slice &
& rv.Type().Elem().Kind() == reflect.Interface { |
| 420 // []interface{}, which is a multi-valued property with a single
name. |
| 421 // Convert to a PropertySlice. |
| 422 nativeValues := rv.Interface().([]interface{}) |
| 423 pslice := make(ds.PropertySlice, len(nativeValues)) |
| 424 for i, nv := range nativeValues { |
| 425 if err = convert(nv, &pslice[i]); err != nil { |
| 426 return |
| 427 } |
| 428 } |
| 429 pdata = pslice |
| 430 return |
| 431 } |
| 432 |
| 433 var prop ds.Property |
| 434 if err = convert(nativeProp.Value, &prop); err != nil { |
| 435 return |
| 436 } |
| 437 pdata = prop |
407 return | 438 return |
408 } | 439 } |
409 | 440 |
410 func (bds *boundDatastore) gaeKeysToNative(keys ...*ds.Key) []*datastore.Key { | 441 func (bds *boundDatastore) gaeKeysToNative(keys ...*ds.Key) []*datastore.Key { |
411 nativeKeys := make([]*datastore.Key, len(keys)) | 442 nativeKeys := make([]*datastore.Key, len(keys)) |
412 for i, key := range keys { | 443 for i, key := range keys { |
413 _, _, toks := key.Split() | 444 _, _, toks := key.Split() |
414 | 445 |
415 var nativeKey *datastore.Key | 446 var nativeKey *datastore.Key |
416 for _, tok := range toks { | 447 for _, tok := range toks { |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
456 | 487 |
457 var _ datastore.PropertyLoadSaver = (*nativePropertyLoadSaver)(nil) | 488 var _ datastore.PropertyLoadSaver = (*nativePropertyLoadSaver)(nil) |
458 | 489 |
459 func (npls *nativePropertyLoadSaver) Load(props []datastore.Property) error { | 490 func (npls *nativePropertyLoadSaver) Load(props []datastore.Property) error { |
460 if npls.pmap == nil { | 491 if npls.pmap == nil { |
461 // Allocate for common case: one property per property name. | 492 // Allocate for common case: one property per property name. |
462 npls.pmap = make(ds.PropertyMap, len(props)) | 493 npls.pmap = make(ds.PropertyMap, len(props)) |
463 } | 494 } |
464 | 495 |
465 for _, nativeProp := range props { | 496 for _, nativeProp := range props { |
466 » » name, props, err := npls.bds.nativePropertyToGAE(nativeProp) | 497 » » name, pdata, err := npls.bds.nativePropertyToGAE(nativeProp) |
467 if err != nil { | 498 if err != nil { |
468 return err | 499 return err |
469 } | 500 } |
470 » » npls.pmap[name] = append(npls.pmap[name], props...) | 501 » » if _, ok := npls.pmap[name]; ok { |
| 502 » » » return fmt.Errorf("duplicate properties for %q", name) |
| 503 » » } |
| 504 » » npls.pmap[name] = pdata |
471 } | 505 } |
472 return nil | 506 return nil |
473 } | 507 } |
474 | 508 |
475 func (npls *nativePropertyLoadSaver) Save() ([]datastore.Property, error) { | 509 func (npls *nativePropertyLoadSaver) Save() ([]datastore.Property, error) { |
476 if len(npls.pmap) == 0 { | 510 if len(npls.pmap) == 0 { |
477 return nil, nil | 511 return nil, nil |
478 } | 512 } |
479 | 513 |
480 props := make([]datastore.Property, 0, len(npls.pmap)) | 514 props := make([]datastore.Property, 0, len(npls.pmap)) |
481 » for name, plist := range npls.pmap { | 515 » for name, pdata := range npls.pmap { |
482 // Strip meta. | 516 // Strip meta. |
483 if strings.HasPrefix(name, "$") { | 517 if strings.HasPrefix(name, "$") { |
484 continue | 518 continue |
485 } | 519 } |
486 | 520 |
487 » » nativeProp, err := npls.bds.gaePropertyToNative(name, plist) | 521 » » nativeProp, err := npls.bds.gaePropertyToNative(name, pdata) |
488 if err != nil { | 522 if err != nil { |
489 return nil, err | 523 return nil, err |
490 } | 524 } |
491 props = append(props, nativeProp) | 525 props = append(props, nativeProp) |
492 } | 526 } |
493 return props, nil | 527 return props, nil |
494 } | 528 } |
495 | 529 |
496 var datastoreTransactionKey = "*datastore.Transaction" | 530 var datastoreTransactionKey = "*datastore.Transaction" |
497 | 531 |
498 func withDatastoreTransaction(c context.Context, tx *datastore.Transaction) cont
ext.Context { | 532 func withDatastoreTransaction(c context.Context, tx *datastore.Transaction) cont
ext.Context { |
499 return context.WithValue(c, &datastoreTransactionKey, tx) | 533 return context.WithValue(c, &datastoreTransactionKey, tx) |
500 } | 534 } |
501 | 535 |
502 func datastoreTransaction(c context.Context) *datastore.Transaction { | 536 func datastoreTransaction(c context.Context) *datastore.Transaction { |
503 if tx, ok := c.Value(&datastoreTransactionKey).(*datastore.Transaction);
ok { | 537 if tx, ok := c.Value(&datastoreTransactionKey).(*datastore.Transaction);
ok { |
504 return tx | 538 return tx |
505 } | 539 } |
506 return nil | 540 return nil |
507 } | 541 } |
508 | 542 |
509 func clonePropertyMap(pmap ds.PropertyMap) ds.PropertyMap { | 543 func clonePropertyMap(pmap ds.PropertyMap) ds.PropertyMap { |
510 if pmap == nil { | 544 if pmap == nil { |
511 return nil | 545 return nil |
512 } | 546 } |
513 | 547 |
514 clone := make(ds.PropertyMap, len(pmap)) | 548 clone := make(ds.PropertyMap, len(pmap)) |
515 » for k, props := range pmap { | 549 » for k, pdata := range pmap { |
516 » » clone[k] = append([]ds.Property(nil), props...) | 550 » » clone[k] = pdata.Clone() |
517 } | 551 } |
518 return clone | 552 return clone |
519 } | 553 } |
520 | 554 |
521 func normalizeError(err error) error { | 555 func normalizeError(err error) error { |
522 switch err { | 556 switch err { |
523 case datastore.ErrNoSuchEntity: | 557 case datastore.ErrNoSuchEntity: |
524 return ds.ErrNoSuchEntity | 558 return ds.ErrNoSuchEntity |
525 case datastore.ErrConcurrentTransaction: | 559 case datastore.ErrConcurrentTransaction: |
526 return ds.ErrConcurrentTransaction | 560 return ds.ErrConcurrentTransaction |
527 case datastore.ErrInvalidKey: | 561 case datastore.ErrInvalidKey: |
528 return ds.ErrInvalidKey | 562 return ds.ErrInvalidKey |
529 default: | 563 default: |
530 return err | 564 return err |
531 } | 565 } |
532 } | 566 } |
OLD | NEW |