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 |