Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(436)

Side by Side Diff: impl/memory/datastore_query.go

Issue 1312433013: Switch to external stringset implementation. (Closed) Base URL: https://github.com/luci/gae.git@master
Patch Set: fix nitish Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « impl/memory/datastore_index_selection.go ('k') | impl/memory/datastore_query_execution.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « impl/memory/datastore_index_selection.go ('k') | impl/memory/datastore_query_execution.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698