| 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 |