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