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 memory | 5 package memory |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
9 "encoding/base64" | 9 "encoding/base64" |
10 "errors" | 10 "errors" |
11 "fmt" | 11 "fmt" |
12 "math" | 12 "math" |
13 "strings" | 13 "strings" |
14 | 14 |
15 ds "github.com/luci/gae/service/datastore" | 15 ds "github.com/luci/gae/service/datastore" |
16 "github.com/luci/gae/service/datastore/serialize" | 16 "github.com/luci/gae/service/datastore/serialize" |
17 "github.com/luci/luci-go/common/cmpbin" | 17 "github.com/luci/luci-go/common/cmpbin" |
| 18 "github.com/luci/luci-go/common/stringset" |
18 ) | 19 ) |
19 | 20 |
20 // MaxQueryComponents was lifted from a hard-coded constant in dev_appserver. | 21 // MaxQueryComponents was lifted from a hard-coded constant in dev_appserver. |
21 // No idea if it's a real limit or just a convenience in the current dev | 22 // No idea if it's a real limit or just a convenience in the current dev |
22 // appserver implementation. | 23 // appserver implementation. |
23 const MaxQueryComponents = 100 | 24 const MaxQueryComponents = 100 |
24 | 25 |
25 var errQueryDone = errors.New("query is done") | 26 var errQueryDone = errors.New("query is done") |
26 | 27 |
27 type queryOp int | 28 type queryOp int |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 return false | 159 return false |
159 } | 160 } |
160 | 161 |
161 type queryImpl struct { | 162 type queryImpl struct { |
162 ns string | 163 ns string |
163 | 164 |
164 kind string | 165 kind string |
165 | 166 |
166 // prop -> encoded values (which are ds.Property objects) | 167 // prop -> encoded values (which are ds.Property objects) |
167 // "__ancestor__" is the key for Ancestor queries. | 168 // "__ancestor__" is the key for Ancestor queries. |
168 » eqFilters map[string]stringSet | 169 » eqFilters map[string]stringset.Set |
169 ineqFilter queryIneqFilter | 170 ineqFilter queryIneqFilter |
170 order []ds.IndexColumn | 171 order []ds.IndexColumn |
171 startCursor []byte | 172 startCursor []byte |
172 startCursorColumns []ds.IndexColumn | 173 startCursorColumns []ds.IndexColumn |
173 endCursor []byte | 174 endCursor []byte |
174 endCursorColumns []ds.IndexColumn | 175 endCursorColumns []ds.IndexColumn |
175 | 176 |
176 // All of these are applied in post (e.g. not during the native index sc
an). | 177 // All of these are applied in post (e.g. not during the native index sc
an). |
177 distinct bool | 178 distinct bool |
178 eventualConsistency bool | 179 eventualConsistency bool |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
219 MaxQueryComponents, q.numComponents()) | 220 MaxQueryComponents, q.numComponents()) |
220 } | 221 } |
221 if len(q.project) == 0 && q.distinct { | 222 if len(q.project) == 0 && q.distinct { |
222 // This must be delayed, because q.Distinct().Project("foo") is
a valid | 223 // This must be delayed, because q.Distinct().Project("foo") is
a valid |
223 // construction. If we checked this in Distinct, it could be too
early, and | 224 // construction. If we checked this in Distinct, it could be too
early, and |
224 // checking it in Project doesn't matter. | 225 // checking it in Project doesn't matter. |
225 return nil, errors.New( | 226 return nil, errors.New( |
226 "gae/memory: Distinct() only makes sense on projection q
ueries.") | 227 "gae/memory: Distinct() only makes sense on projection q
ueries.") |
227 } | 228 } |
228 if q.eqFilters["__ancestor__"] != nil && q.ineqFilter.prop == "__key__"
{ | 229 if q.eqFilters["__ancestor__"] != nil && q.ineqFilter.prop == "__key__"
{ |
229 » » anc := []byte(nil) | 230 » » ancS, _ := q.eqFilters["__ancestor__"].Peek() |
230 » » for k := range q.eqFilters["__ancestor__"] { | 231 » » anc := []byte(ancS[:len(ancS)-1]) |
231 » » » anc = []byte(k) | |
232 » » » break | |
233 » » } | |
234 » » anc = anc[:len(anc)-1] | |
235 if q.ineqFilter.start != nil && !bytes.HasPrefix(q.ineqFilter.st
art, anc) { | 232 if q.ineqFilter.start != nil && !bytes.HasPrefix(q.ineqFilter.st
art, anc) { |
236 return nil, errors.New( | 233 return nil, errors.New( |
237 "gae/memory: __key__ inequality filter has a val
ue outside of Ancestor()") | 234 "gae/memory: __key__ inequality filter has a val
ue outside of Ancestor()") |
238 } | 235 } |
239 if q.ineqFilter.end != nil && !bytes.HasPrefix(q.ineqFilter.end,
anc) { | 236 if q.ineqFilter.end != nil && !bytes.HasPrefix(q.ineqFilter.end,
anc) { |
240 return nil, errors.New( | 237 return nil, errors.New( |
241 "gae/memory: __key__ inequality filter has a val
ue outside of Ancestor()") | 238 "gae/memory: __key__ inequality filter has a val
ue outside of Ancestor()") |
242 } | 239 } |
243 } | 240 } |
244 | 241 |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
349 // overlapping range ends, then we don't have anything to do. | 346 // overlapping range ends, then we don't have anything to do. |
350 if ret.end != nil && bytes.Compare(ret.start, ret.end) >= 0 { | 347 if ret.end != nil && bytes.Compare(ret.start, ret.end) >= 0 { |
351 return nil, errQueryDone | 348 return nil, errQueryDone |
352 } | 349 } |
353 | 350 |
354 ret.numCols = len(ret.suffixFormat) | 351 ret.numCols = len(ret.suffixFormat) |
355 for prop, vals := range ret.eqFilters { | 352 for prop, vals := range ret.eqFilters { |
356 if len(ret.suffixFormat) == 1 && prop == "__ancestor__" { | 353 if len(ret.suffixFormat) == 1 && prop == "__ancestor__" { |
357 continue | 354 continue |
358 } | 355 } |
359 » » ret.numCols += len(vals) | 356 » » ret.numCols += vals.Len() |
360 } | 357 } |
361 | 358 |
362 return ret, nil | 359 return ret, nil |
363 } | 360 } |
364 | 361 |
365 func (q *queryImpl) numComponents() int { | 362 func (q *queryImpl) numComponents() int { |
366 numComponents := len(q.order) | 363 numComponents := len(q.order) |
367 if q.ineqFilter.prop != "" { | 364 if q.ineqFilter.prop != "" { |
368 if q.ineqFilter.start != nil { | 365 if q.ineqFilter.start != nil { |
369 numComponents++ | 366 numComponents++ |
370 } | 367 } |
371 if q.ineqFilter.end != nil { | 368 if q.ineqFilter.end != nil { |
372 numComponents++ | 369 numComponents++ |
373 } | 370 } |
374 } | 371 } |
375 for _, v := range q.eqFilters { | 372 for _, v := range q.eqFilters { |
376 » » numComponents += len(v) | 373 » » numComponents += v.Len() |
377 } | 374 } |
378 return numComponents | 375 return numComponents |
379 } | 376 } |
380 | 377 |
381 // checkMutateClone sees if the query has an error. If not, it clones the query, | 378 // checkMutateClone sees if the query has an error. If not, it clones the query, |
382 // and assigns the output of `check` to the query error slot. If check returns | 379 // and assigns the output of `check` to the query error slot. If check returns |
383 // nil, it calls `mutate` on the cloned query. The (possibly new) query is then | 380 // nil, it calls `mutate` on the cloned query. The (possibly new) query is then |
384 // returned. | 381 // returned. |
385 func (q *queryImpl) checkMutateClone(check func() error, mutate func(*queryImpl)
) *queryImpl { | 382 func (q *queryImpl) checkMutateClone(check func() error, mutate func(*queryImpl)
) *queryImpl { |
386 if q.err != nil { | 383 if q.err != nil { |
387 return q | 384 return q |
388 } | 385 } |
389 nq := *q | 386 nq := *q |
390 » nq.eqFilters = make(map[string]stringSet, len(q.eqFilters)) | 387 » nq.eqFilters = make(map[string]stringset.Set, len(q.eqFilters)) |
391 for prop, vals := range q.eqFilters { | 388 for prop, vals := range q.eqFilters { |
392 » » nq.eqFilters[prop] = vals.dup() | 389 » » nq.eqFilters[prop] = vals.Dup() |
393 } | 390 } |
394 nq.order = make([]ds.IndexColumn, len(q.order)) | 391 nq.order = make([]ds.IndexColumn, len(q.order)) |
395 copy(nq.order, q.order) | 392 copy(nq.order, q.order) |
396 nq.project = make([]string, len(q.project)) | 393 nq.project = make([]string, len(q.project)) |
397 copy(nq.project, q.project) | 394 copy(nq.project, q.project) |
398 if check != nil { | 395 if check != nil { |
399 nq.err = check() | 396 nq.err = check() |
400 } | 397 } |
401 if nq.err == nil { | 398 if nq.err == nil { |
402 mutate(&nq) | 399 mutate(&nq) |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
434 | 431 |
435 func (q *queryImpl) Distinct() ds.Query { | 432 func (q *queryImpl) Distinct() ds.Query { |
436 return q.checkMutateClone(nil, func(q *queryImpl) { | 433 return q.checkMutateClone(nil, func(q *queryImpl) { |
437 q.distinct = true | 434 q.distinct = true |
438 }) | 435 }) |
439 } | 436 } |
440 | 437 |
441 func (q *queryImpl) addEqFilt(prop string, p ds.Property) { | 438 func (q *queryImpl) addEqFilt(prop string, p ds.Property) { |
442 binVal := string(serialize.ToBytes(p)) | 439 binVal := string(serialize.ToBytes(p)) |
443 if cur, ok := q.eqFilters[prop]; !ok { | 440 if cur, ok := q.eqFilters[prop]; !ok { |
444 » » q.eqFilters[prop] = stringSet{binVal: {}} | 441 » » s := stringset.New(1) |
| 442 » » s.Add(binVal) |
| 443 » » q.eqFilters[prop] = s |
445 } else { | 444 } else { |
446 » » cur.add(binVal) | 445 » » cur.Add(binVal) |
447 } | 446 } |
448 } | 447 } |
449 | 448 |
450 func (q *queryImpl) Filter(fStr string, val interface{}) ds.Query { | 449 func (q *queryImpl) Filter(fStr string, val interface{}) ds.Query { |
451 prop := "" | 450 prop := "" |
452 op := qInvalid | 451 op := qInvalid |
453 p := ds.Property{} | 452 p := ds.Property{} |
454 return q.checkMutateClone( | 453 return q.checkMutateClone( |
455 func() error { | 454 func() error { |
456 var err error | 455 var err error |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
577 q.order = append(q.order, col) | 576 q.order = append(q.order, col) |
578 }) | 577 }) |
579 } | 578 } |
580 | 579 |
581 func (q *queryImpl) Project(fieldName ...string) ds.Query { | 580 func (q *queryImpl) Project(fieldName ...string) ds.Query { |
582 return q.checkMutateClone( | 581 return q.checkMutateClone( |
583 func() error { | 582 func() error { |
584 if q.keysOnly { | 583 if q.keysOnly { |
585 return errors.New("cannot project a keysOnly que
ry") | 584 return errors.New("cannot project a keysOnly que
ry") |
586 } | 585 } |
587 » » » dupCheck := stringSet{} | 586 » » » dupCheck := stringset.New(len(fieldName) + len(q.project
)) |
588 for _, f := range fieldName { | 587 for _, f := range fieldName { |
589 » » » » if !dupCheck.add(f) { | 588 » » » » if !dupCheck.Add(f) { |
590 return fmt.Errorf("cannot project on the
same field twice: %q", f) | 589 return fmt.Errorf("cannot project on the
same field twice: %q", f) |
591 } | 590 } |
592 if f == "" { | 591 if f == "" { |
593 return errors.New("cannot project on an
empty field name") | 592 return errors.New("cannot project on an
empty field name") |
594 } | 593 } |
595 if f == "__key__" { | 594 if f == "__key__" { |
596 return fmt.Errorf("cannot project on __k
ey__") | 595 return fmt.Errorf("cannot project on __k
ey__") |
597 } | 596 } |
598 if _, ok := q.eqFilters[f]; ok { | 597 if _, ok := q.eqFilters[f]; ok { |
599 return fmt.Errorf( | 598 return fmt.Errorf( |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
694 q.endCursor = curs | 693 q.endCursor = curs |
695 }) | 694 }) |
696 } | 695 } |
697 | 696 |
698 func (q *queryImpl) EventualConsistency() ds.Query { | 697 func (q *queryImpl) EventualConsistency() ds.Query { |
699 return q.checkMutateClone( | 698 return q.checkMutateClone( |
700 nil, func(q *queryImpl) { | 699 nil, func(q *queryImpl) { |
701 q.eventualConsistency = true | 700 q.eventualConsistency = true |
702 }) | 701 }) |
703 } | 702 } |
OLD | NEW |