Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package datastore | 5 package datastore |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "bytes" | 8 "bytes" |
| 9 "fmt" | 9 "fmt" |
| 10 "sort" | 10 "sort" |
| 11 "strings" | 11 "strings" |
| 12 "sync" | |
| 12 | 13 |
| 13 "github.com/luci/luci-go/common/errors" | 14 "github.com/luci/luci-go/common/errors" |
| 14 "github.com/luci/luci-go/common/stringset" | 15 "github.com/luci/luci-go/common/stringset" |
| 15 ) | 16 ) |
| 16 | 17 |
| 17 var ( | 18 var ( |
| 18 // ErrMultipleInequalityFilter is returned from Query.Finalize if you bu ild a | 19 // ErrMultipleInequalityFilter is returned from Query.Finalize if you bu ild a |
| 19 // query which has inequality filters on multiple fields. | 20 // query which has inequality filters on multiple fields. |
| 20 ErrMultipleInequalityFilter = errors.New( | 21 ErrMultipleInequalityFilter = errors.New( |
| 21 "inequality filters on multiple properties in the same Query is not allowed") | 22 "inequality filters on multiple properties in the same Query is not allowed") |
| 22 | 23 |
| 23 // ErrNullQuery is returned from Query.Finalize if you build a query for which | 24 // ErrNullQuery is returned from Query.Finalize if you build a query for which |
| 24 // there cannot possibly be any results. | 25 // there cannot possibly be any results. |
| 25 ErrNullQuery = errors.New( | 26 ErrNullQuery = errors.New( |
| 26 "the query is overconstrained and can never have results") | 27 "the query is overconstrained and can never have results") |
| 27 ) | 28 ) |
| 28 | 29 |
| 29 // Query is a builder-object for building a datastore query. It may represent | 30 // Query is a builder-object for building a datastore query. It may represent |
| 30 // an invalid query, but the error will only be observable when you call | 31 // an invalid query, but the error will only be observable when you call |
| 31 // Finalize. | 32 // Finalize. |
| 33 // | |
| 34 // A Query is, for the most part, not goroutine-safe. However, it is | |
| 32 type Query struct { | 35 type Query struct { |
| 36 queryFields | |
| 37 | |
| 38 // These are set by Finalize as a way to cache the 1-1 correspondence of | |
| 39 // a Query to its FinalizedQuery form. err may also be set by intermedia te | |
| 40 // Query functions if there's a problem before finalization. | |
| 41 // | |
| 42 // Query implements lazy finalization, meaning that it will happen at mo st | |
| 43 // once. This means that the finalization state and cached finalization must | |
| 44 // be locked around. | |
| 45 finalizeOnce sync.Once | |
| 46 finalized *FinalizedQuery | |
| 47 finalizeErr error | |
| 48 } | |
| 49 | |
| 50 // queryFields are the Query's read-only fields. | |
| 51 type queryFields struct { | |
|
dnj
2016/04/21 22:49:16
Why!?
Because we clone the query by copying it, m
| |
| 33 kind string | 52 kind string |
| 34 | 53 |
| 35 eventualConsistency bool | 54 eventualConsistency bool |
| 36 keysOnly bool | 55 keysOnly bool |
| 37 distinct bool | 56 distinct bool |
| 38 | 57 |
| 39 limit *int32 | 58 limit *int32 |
| 40 offset *int32 | 59 offset *int32 |
| 41 | 60 |
| 42 order []IndexColumn | 61 order []IndexColumn |
| 43 project stringset.Set | 62 project stringset.Set |
| 44 | 63 |
| 45 eqFilts map[string]PropertySlice | 64 eqFilts map[string]PropertySlice |
| 46 | 65 |
| 47 ineqFiltProp string | 66 ineqFiltProp string |
| 48 ineqFiltLow Property | 67 ineqFiltLow Property |
| 49 ineqFiltLowIncl bool | 68 ineqFiltLowIncl bool |
| 50 ineqFiltLowSet bool | 69 ineqFiltLowSet bool |
| 51 ineqFiltHigh Property | 70 ineqFiltHigh Property |
| 52 ineqFiltHighIncl bool | 71 ineqFiltHighIncl bool |
| 53 ineqFiltHighSet bool | 72 ineqFiltHighSet bool |
| 54 | 73 |
| 55 start Cursor | 74 start Cursor |
| 56 end Cursor | 75 end Cursor |
| 57 | 76 |
| 58 » // These are set by Finalize as a way to cache the 1-1 correspondence of | 77 » err error |
| 59 » // a Query to its FinalizedQuery form. err may also be set by intermedia te | |
| 60 » // Query functions if there's a problem before finalization. | |
| 61 » finalized *FinalizedQuery | |
| 62 » err error | |
| 63 } | 78 } |
| 64 | 79 |
| 65 // NewQuery returns a new Query for the given kind. If kind may be empty to | 80 // NewQuery returns a new Query for the given kind. If kind may be empty to |
| 66 // begin a kindless query. | 81 // begin a kindless query. |
| 67 func NewQuery(kind string) *Query { | 82 func NewQuery(kind string) *Query { |
| 68 » return &Query{kind: kind} | 83 » return &Query{ |
| 84 » » queryFields: queryFields{ | |
| 85 » » » kind: kind, | |
| 86 » » }, | |
| 87 » } | |
| 69 } | 88 } |
| 70 | 89 |
| 71 func (q *Query) mod(cb func(*Query)) *Query { | 90 func (q *Query) mod(cb func(*Query)) *Query { |
| 72 if q.err != nil { | 91 if q.err != nil { |
| 73 return q | 92 return q |
| 74 } | 93 } |
| 75 | 94 |
| 76 » ret := *q | 95 » ret := Query{ |
| 77 » ret.finalized = nil | 96 » » queryFields: q.queryFields, |
| 97 » } | |
| 78 if len(q.order) > 0 { | 98 if len(q.order) > 0 { |
| 79 ret.order = make([]IndexColumn, len(q.order)) | 99 ret.order = make([]IndexColumn, len(q.order)) |
| 80 copy(ret.order, q.order) | 100 copy(ret.order, q.order) |
| 81 } | 101 } |
| 82 if q.project != nil { | 102 if q.project != nil { |
| 83 ret.project = q.project.Dup() | 103 ret.project = q.project.Dup() |
| 84 } | 104 } |
| 85 if len(q.eqFilts) > 0 { | 105 if len(q.eqFilts) > 0 { |
| 86 ret.eqFilts = make(map[string]PropertySlice, len(q.eqFilts)) | 106 ret.eqFilts = make(map[string]PropertySlice, len(q.eqFilts)) |
| 87 for k, v := range q.eqFilts { | 107 for k, v := range q.eqFilts { |
| (...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 461 } | 481 } |
| 462 q.ineqFiltLowSet = false | 482 q.ineqFiltLowSet = false |
| 463 q.ineqFiltHighSet = false | 483 q.ineqFiltHighSet = false |
| 464 }) | 484 }) |
| 465 } | 485 } |
| 466 | 486 |
| 467 // Finalize converts this Query to a FinalizedQuery. If the Query has any | 487 // Finalize converts this Query to a FinalizedQuery. If the Query has any |
| 468 // inconsistencies or violates any of the query rules, that will be returned | 488 // inconsistencies or violates any of the query rules, that will be returned |
| 469 // here. | 489 // here. |
| 470 func (q *Query) Finalize() (*FinalizedQuery, error) { | 490 func (q *Query) Finalize() (*FinalizedQuery, error) { |
| 471 » if q.err != nil || q.finalized != nil { | 491 » if q.err != nil { |
| 472 » » return q.finalized, q.err | 492 » » return nil, q.err |
| 473 } | 493 } |
| 474 | 494 |
| 495 q.finalizeOnce.Do(func() { | |
| 496 q.finalized, q.finalizeErr = q.finalizeImpl() | |
| 497 }) | |
| 498 return q.finalized, q.finalizeErr | |
| 499 } | |
| 500 | |
| 501 func (q *Query) finalizeImpl() (*FinalizedQuery, error) { | |
| 475 ancestor := (*Key)(nil) | 502 ancestor := (*Key)(nil) |
| 476 if slice, ok := q.eqFilts["__ancestor__"]; ok { | 503 if slice, ok := q.eqFilts["__ancestor__"]; ok { |
| 477 ancestor = slice[0].Value().(*Key) | 504 ancestor = slice[0].Value().(*Key) |
| 478 } | 505 } |
| 479 | 506 |
| 480 err := func() error { | 507 err := func() error { |
| 481 | 508 |
| 482 if q.kind == "" { // kindless query checks | 509 if q.kind == "" { // kindless query checks |
| 483 if q.ineqFiltProp != "" && q.ineqFiltProp != "__key__" { | 510 if q.ineqFiltProp != "" && q.ineqFiltProp != "__key__" { |
| 484 return fmt.Errorf( | 511 return fmt.Errorf( |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 537 if _, iseq := q.eqFilts[p]; iseq { | 564 if _, iseq := q.eqFilts[p]; iseq { |
| 538 err = fmt.Errorf("cannot project on equa lity filter field: %s", p) | 565 err = fmt.Errorf("cannot project on equa lity filter field: %s", p) |
| 539 return false | 566 return false |
| 540 } | 567 } |
| 541 return true | 568 return true |
| 542 }) | 569 }) |
| 543 } | 570 } |
| 544 return err | 571 return err |
| 545 }() | 572 }() |
| 546 if err != nil { | 573 if err != nil { |
| 547 q.err = err | |
| 548 return nil, err | 574 return nil, err |
| 549 } | 575 } |
| 550 | 576 |
| 551 ret := &FinalizedQuery{ | 577 ret := &FinalizedQuery{ |
| 552 original: q, | 578 original: q, |
| 553 kind: q.kind, | 579 kind: q.kind, |
| 554 | 580 |
| 555 keysOnly: q.keysOnly, | 581 keysOnly: q.keysOnly, |
| 556 eventuallyConsistent: q.eventualConsistency || ancestor == nil, | 582 eventuallyConsistent: q.eventualConsistency || ancestor == nil, |
| 557 limit: q.limit, | 583 limit: q.limit, |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 637 } | 663 } |
| 638 } | 664 } |
| 639 | 665 |
| 640 // If the suffix format ends with __key__ already (e.g. .Order("__key__" )), | 666 // If the suffix format ends with __key__ already (e.g. .Order("__key__" )), |
| 641 // then we're good to go. Otherwise we need to add it as the last bit of the | 667 // then we're good to go. Otherwise we need to add it as the last bit of the |
| 642 // suffix, since all indexes implicitly have it as the last column. | 668 // suffix, since all indexes implicitly have it as the last column. |
| 643 if len(ret.orders) == 0 || ret.orders[len(ret.orders)-1].Property != "__ key__" { | 669 if len(ret.orders) == 0 || ret.orders[len(ret.orders)-1].Property != "__ key__" { |
| 644 ret.orders = append(ret.orders, IndexColumn{Property: "__key__"} ) | 670 ret.orders = append(ret.orders, IndexColumn{Property: "__key__"} ) |
| 645 } | 671 } |
| 646 | 672 |
| 647 q.finalized = ret | |
| 648 return ret, nil | 673 return ret, nil |
| 649 } | 674 } |
| 650 | 675 |
| 651 func (q *Query) String() string { | 676 func (q *Query) String() string { |
| 652 ret := &bytes.Buffer{} | 677 ret := &bytes.Buffer{} |
| 653 needComma := false | 678 needComma := false |
| 654 p := func(fmtStr string, stuff ...interface{}) { | 679 p := func(fmtStr string, stuff ...interface{}) { |
| 655 if needComma { | 680 if needComma { |
| 656 if _, err := ret.WriteString(", "); err != nil { | 681 if _, err := ret.WriteString(", "); err != nil { |
| 657 panic(err) | 682 panic(err) |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 738 if q.keysOnly { | 763 if q.keysOnly { |
| 739 p("KeysOnly") | 764 p("KeysOnly") |
| 740 } | 765 } |
| 741 | 766 |
| 742 if _, err := ret.WriteRune(')'); err != nil { | 767 if _, err := ret.WriteRune(')'); err != nil { |
| 743 panic(err) | 768 panic(err) |
| 744 } | 769 } |
| 745 | 770 |
| 746 return ret.String() | 771 return ret.String() |
| 747 } | 772 } |
| OLD | NEW |