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

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

Issue 1302813003: impl/memory: Implement Queries (Closed) Base URL: https://github.com/luci/gae.git@add_multi_iterator
Patch Set: Baby's first query! 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
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 "encoding/base64" 9 "encoding/base64"
9 "errors" 10 "errors"
10 "fmt" 11 "fmt"
11 "math" 12 "math"
12 "strings" 13 "strings"
13 14
14 ds "github.com/luci/gae/service/datastore" 15 ds "github.com/luci/gae/service/datastore"
15 "github.com/luci/gae/service/datastore/serialize" 16 "github.com/luci/gae/service/datastore/serialize"
17 "github.com/luci/luci-go/common/cmpbin"
16 ) 18 )
17 19
18 // MaxQueryComponents was lifted from a hard-coded constant in dev_appserver. 20 // MaxQueryComponents was lifted from a hard-coded constant in dev_appserver.
19 // No idea if it's a real limit or just a convenience in the current dev 21 // No idea if it's a real limit or just a convenience in the current dev
20 // appserver implementation. 22 // appserver implementation.
21 const MaxQueryComponents = 100 23 const MaxQueryComponents = 100
22 24
23 var errQueryDone = errors.New("query is done") 25 var errQueryDone = errors.New("query is done")
24 26
25 type queryOp int 27 type queryOp int
(...skipping 29 matching lines...) Expand all
55 op = queryOpMap[toks[1]] 57 op = queryOpMap[toks[1]]
56 if op == qInvalid { 58 if op == qInvalid {
57 err = fmt.Errorf("datastore: invalid operator %q in filt er %q", toks[1], f) 59 err = fmt.Errorf("datastore: invalid operator %q in filt er %q", toks[1], f)
58 } else { 60 } else {
59 prop = toks[0] 61 prop = toks[0]
60 } 62 }
61 } 63 }
62 return 64 return
63 } 65 }
64 66
65 type queryCursor string 67 // A queryCursor is:
68 // {#orders} ++ IndexColumn* ++ RawRowData
69 // IndexColumn will always contain __key__ as the last column, and so #orders
dnj (Google) 2015/08/23 06:50:06 Do we need to encode the key column, then, or can
iannucci 2015/08/23 18:19:42 Yeah, we do, because it may not have an ASCENDING
70 // must always be >= 1
71 type queryCursor []byte
72
73 func newCursor(s string) (ds.Cursor, error) {
74 » d, err := base64.URLEncoding.DecodeString(s)
dnj (Google) 2015/08/23 06:50:06 Ack - I suspect that the recent cursor change I co
iannucci 2015/08/23 18:19:42 Nah, I think it's fine here. This is parsing a cur
75 » if err != nil {
76 » » return nil, fmt.Errorf("Failed to Base64-decode cursor: %s", err )
77 » }
78 » c := queryCursor(d)
79 » if _, _, err := c.decode(); err != nil {
80 » » return nil, err
81 » }
82 » return c, nil
83 }
66 84
67 func (q queryCursor) String() string { return base64.URLEncoding.EncodeToString( []byte(q)) } 85 func (q queryCursor) String() string { return base64.URLEncoding.EncodeToString( []byte(q)) }
68 func (q queryCursor) Valid() bool { return q != "" } 86
87 // decode returns the encoded IndexColumns, the raw row (cursor) data, or an
88 // error.
89 func (q queryCursor) decode() ([]ds.IndexColumn, []byte, error) {
90 » buf := bytes.NewBuffer([]byte(q))
91 » count, _, err := cmpbin.ReadUint(buf)
92 » if err != nil {
93 » » return nil, nil, fmt.Errorf("invalid cursor")
dnj (Google) 2015/08/23 06:50:06 Since you're generating errors here, you should be
iannucci 2015/08/23 18:19:42 Done.
94 » }
95
96 » if count > ds.MaxIndexColumns {
dnj (Google) 2015/08/23 06:50:06 if count <= 0 || count > ds.MaxIndexColumns { re
iannucci 2015/08/23 18:19:42 Done.
97 » » return nil, nil, fmt.Errorf("invalid cursor")
98 » }
99
100 » if count <= 0 {
101 » » return nil, nil, fmt.Errorf("invalid cursor")
102 » }
103
104 » cols := make([]ds.IndexColumn, count)
105 » for i := range cols {
106 » » if cols[i], err = serialize.ReadIndexColumn(buf); err != nil {
107 » » » return nil, nil, fmt.Errorf("invalid cursor")
dnj (Google) 2015/08/23 06:50:06 Note which index failed and why in the returned er
iannucci 2015/08/23 18:19:42 Done.
108 » » }
109 » }
110
111 » if cols[len(cols)-1].Property != "__key__" {
112 » » return nil, nil, fmt.Errorf("invalid cursor")
dnj (Google) 2015/08/23 06:50:06 "column set must end with the key". If key is impl
iannucci 2015/08/23 18:19:42 Acknowledged.
113 » }
114
115 » return cols, buf.Bytes(), nil
116 }
69 117
70 type queryIneqFilter struct { 118 type queryIneqFilter struct {
71 prop string 119 prop string
72 120
73 » low *string 121 » start []byte
74 » high *string 122 » end []byte
75 }
76
77 func decodeCursor(s string) (ds.Cursor, error) {
78 » d, err := base64.URLEncoding.DecodeString(s)
79 » if err != nil {
80 » » return nil, fmt.Errorf("Failed to Base64-decode cursor: %s", err )
81 » }
82 » c := queryCursor(string(d))
83 » if !c.Valid() {
84 » » return nil, errors.New("Decoded cursor is not valid.")
85 » }
86 » return c, nil
87 }
88
89 func increment(bstr string, positive bool) string {
90 » lastIdx := len(bstr) - 1
91 » last := bstr[lastIdx]
92 » if positive {
93 » » if last == 0xFF {
94 » » » return bstr + "\x00"
95 » » }
96 » » return bstr[:lastIdx] + string([]byte{last + 1})
97 » } else {
98 » » if last == 0 {
99 » » » return bstr[:lastIdx-1]
100 » » }
101 » » return bstr[:lastIdx] + string([]byte{last - 1})
102 » }
103 } 123 }
104 124
105 // constrain 'folds' a new inequality into the current inequality filter. 125 // constrain 'folds' a new inequality into the current inequality filter.
106 // 126 //
107 // It will bump the high bound down, or the low bound up, assuming the incoming 127 // It will bump the end bound down, or the start bound up, assuming the incoming
108 // constraint does so. 128 // constraint does so.
109 // 129 //
110 // It returns true iff the filter is overconstrained (i.e. low > high) 130 // It returns true iff the filter is overconstrained (i.e. start > end)
111 func (q *queryIneqFilter) constrain(op queryOp, val string) bool { 131 func (q *queryIneqFilter) constrain(op queryOp, val []byte) bool {
112 switch op { 132 switch op {
133 case qLessEq:
134 val = increment(val)
135 fallthrough
113 case qLessThan: 136 case qLessThan:
114 val = increment(val, true)
115 fallthrough
116 case qLessEq:
117 // adjust upper bound downwards 137 // adjust upper bound downwards
118 » » if q.high == nil || *q.high > val { 138 » » if q.end == nil || bytes.Compare(q.end, val) > 0 {
119 » » » q.high = &val 139 » » » q.end = val
120 } 140 }
121 141
122 case qGreaterThan: 142 case qGreaterThan:
123 » » val = increment(val, false) 143 » » val = increment(val)
124 fallthrough 144 fallthrough
125 case qGreaterEq: 145 case qGreaterEq:
126 // adjust lower bound upwards 146 // adjust lower bound upwards
127 » » if q.low == nil || *q.low < val { 147 » » if q.start == nil || bytes.Compare(q.start, val) < 0 {
128 » » » q.low = &val 148 » » » q.start = val
129 } 149 }
130 150
131 default: 151 default:
132 » » panic(fmt.Errorf("constrain cannot handle filter op %d", op)) 152 » » impossible(fmt.Errorf("constrain cannot handle filter op %d", op ))
133 } 153 }
134 154
135 » if q.low != nil && q.high != nil { 155 » if q.start != nil && q.end != nil {
136 » » return *q.low > *q.high 156 » » return bytes.Compare(q.start, q.end) >= 0
137 } 157 }
138 return false 158 return false
139 } 159 }
140 160
141 type queryImpl struct { 161 type queryImpl struct {
142 ns string 162 ns string
143 163
144 » kind string 164 » kind string
145 » ancestor ds.Key
146 165
147 » // prop -> encoded values 166 » // prop -> encoded values (which are ds.Property objects)
148 » eqFilters map[string]map[string]struct{} 167 » // "__ancestor__" is the key for Ancestor queries.
149 » ineqFilter queryIneqFilter 168 » eqFilters map[string]map[string]struct{}
150 » order []ds.IndexColumn 169 » ineqFilter queryIneqFilter
151 » project map[string]struct{} 170 » order []ds.IndexColumn
171 » startCursor []byte
172 » startCursorColumns []ds.IndexColumn
173 » endCursor []byte
174 » endCursorColumns []ds.IndexColumn
152 175
176 // All of these are applied in post (e.g. not during the native index sc an).
153 distinct bool 177 distinct bool
154 eventualConsistency bool 178 eventualConsistency bool
155 keysOnly bool 179 keysOnly bool
156 limit int32 180 limit int32
157 offset int32 181 offset int32
158 182 » project []string
159 » start queryCursor
160 » end queryCursor
161 183
162 err error 184 err error
163 } 185 }
164 186
165 var _ ds.Query = (*queryImpl)(nil) 187 var _ ds.Query = (*queryImpl)(nil)
166 188
167 func (q *queryImpl) valid(ns string, isTxn bool) (done bool, err error) { 189 func sortOrdersEqual(as, bs []ds.IndexColumn) bool {
168 » if q.err == errQueryDone { 190 » if len(as) != len(bs) {
169 » » done = true 191 » » return false
170 » } else if q.err != nil { 192 » }
171 » » err = q.err 193 » for i, a := range as {
172 » } else if ns != q.ns { 194 » » if a != bs[i] {
173 » » err = errors.New( 195 » » » return false
196 » » }
197 » }
198 » return true
199 }
200
201 func (q *queryImpl) reduce(ns string, isTxn bool) (*reducedQuery, error) {
202 » if q.err != nil {
203 » » return nil, q.err
204 » }
205 » if ns != q.ns {
206 » » return nil, errors.New(
174 "gae/memory: Namespace mismatched. Query and Datastore d on't agree " + 207 "gae/memory: Namespace mismatched. Query and Datastore d on't agree " +
175 "on the current namespace") 208 "on the current namespace")
176 » } else if isTxn && q.ancestor == nil { 209 » } else if isTxn && q.eqFilters["__ancestor__"] == nil {
dnj (Google) 2015/08/23 06:50:06 IMO the "else" chaining here is ugly and unnecessa
iannucci 2015/08/23 18:19:42 Done.
177 » » err = errors.New( 210 » » return nil, errors.New(
178 "gae/memory: Only ancestor queries are allowed inside tr ansactions") 211 "gae/memory: Only ancestor queries are allowed inside tr ansactions")
179 } else if q.numComponents() > MaxQueryComponents { 212 } else if q.numComponents() > MaxQueryComponents {
180 » » err = fmt.Errorf( 213 » » return nil, fmt.Errorf(
181 "gae/memory: query is too large. may not have more than "+ 214 "gae/memory: query is too large. may not have more than "+
182 "%d filters + sort orders + ancestor total: had %d", 215 "%d filters + sort orders + ancestor total: had %d",
183 MaxQueryComponents, q.numComponents()) 216 MaxQueryComponents, q.numComponents())
184 } else if len(q.project) == 0 && q.distinct { 217 } else if len(q.project) == 0 && q.distinct {
185 // This must be delayed, because q.Distinct().Project("foo") is a valid 218 // This must be delayed, because q.Distinct().Project("foo") is a valid
186 // construction. If we checked this in Distinct, it could be too early, and 219 // construction. If we checked this in Distinct, it could be too early, and
187 // checking it in Project doesn't matter. 220 // checking it in Project doesn't matter.
188 » » err = errors.New( 221 » » return nil, errors.New(
189 "gae/memory: Distinct() only makes sense on projection q ueries.") 222 "gae/memory: Distinct() only makes sense on projection q ueries.")
223 } else if q.eqFilters["__ancestor__"] != nil && q.ineqFilter.prop == "__ key__" {
224 anc := []byte(nil)
225 for k := range q.eqFilters["__ancestor__"] {
226 anc = []byte(k)
227 break
228 }
229 anc = anc[:len(anc)-1]
230 if q.ineqFilter.start != nil && !bytes.HasPrefix(q.ineqFilter.st art, anc) {
231 return nil, errors.New(
232 "gae/memory: __key__ inequality filter has a val ue outside of Ancestor()")
233 }
234 if q.ineqFilter.end != nil && !bytes.HasPrefix(q.ineqFilter.end, anc) {
235 return nil, errors.New(
236 "gae/memory: __key__ inequality filter has a val ue outside of Ancestor()")
237 }
190 } 238 }
191 » return 239
240 » ret := &reducedQuery{
241 » » ns: q.ns,
242 » » kind: q.kind,
243 » » eqFilters: q.eqFilters,
244 » » suffixFormat: q.order,
245
246 » » start: q.startCursor,
247 » » end: q.endCursor,
248 » }
249
250 » // if len(q.suffixFormat) > 0, queryImpl already enforces that the first order
251 » // is the same as the inequality. Otherwise we need to add it.
252 » if len(ret.suffixFormat) == 0 && q.ineqFilter.prop != "" {
253 » » ret.suffixFormat = []ds.IndexColumn{{Property: q.ineqFilter.prop }}
254 » }
255
256 » // The inequality is specified in natural (ascending) order in the query 's
257 » // Filter syntax, but the order information may indicate to use a descen ding
258 » // index column for it. If that's the case, then we must invert, swap an d
259 » // increment the inequality endpoints.
260 » //
261 » // Invert so that the desired numbers are represented correctly in the i ndex.
262 » // Swap so that our iterators still go from >= start to < end.
263 » // Increment so that >= and < get correctly bounded (since the iterator is
264 » // still using natrual bytes ordering)
265 » if q.ineqFilter.prop != "" && ret.suffixFormat[0].Direction == ds.DESCEN DING {
266 » » hi, lo := increment(invert(q.ineqFilter.end)), increment(invert( q.ineqFilter.start))
267 » » q.ineqFilter.end, q.ineqFilter.start = lo, hi
268 » }
269
270 » // Add any projection columns not mentioned in the user-defined order as
271 » // ASCENDING orders. Technically we could be smart and automatically use
272 » // a DESCENDING ordered index, if it fit, but the logic gets insane, sin ce all
273 » // suffixes of all used indexes need to be PRECISELY equal (and so you'd have
274 » // to hunt/invalidate/something to find the combination of indexes that are
275 » // compatible with each other as well as the query). If you want to use
276 » // a DESCENDING column, just add it to the user sort order, and this loo p will
277 » // not synthesize a new suffix entry for it.
278 » //
279 » // NOTE: if you want to use an index that sorts by -__key__, you MUST
280 » // include all of the projected fields for that index in the order expli citly.
281 » // Otherwise the generated suffixFormat will be wacky. So:
282 » // Query("Foo").Project("A", "B").Order("A").Order("-__key__")
283 » //
284 » // will turn into a suffixFormat of:
285 » // A, ASCENDING
286 » // __key__, DESCENDING
287 » // B, ASCENDING
288 » // __key__, ASCENDING
289 » //
290 » // To prevent this, your query should have another Order("B") clause bef ore
291 » // the -__key__ clause.
292 » originalStop := len(ret.suffixFormat)
293 » for _, p := range q.project {
294 » » needAdd := true
295 » » // originalStop prevents this loop from getting longer every tim e we add
296 » » // a projected property.
297 » » for _, col := range ret.suffixFormat[:originalStop] {
298 » » » if col.Property == p {
299 » » » » needAdd = false
300 » » » » break
301 » » » }
302 » » }
303 » » if needAdd {
304 » » » ret.suffixFormat = append(ret.suffixFormat, ds.IndexColu mn{Property: p})
305 » » }
306 » }
307
308 » // If the suffix format ends with __key__ already (e.g. .Order("__key__" )),
309 » // then we're good to go. Otherwise we need to add it as the last bit of the
310 » // suffix, since all indexes implicitly have it as the last column.
311 » if len(ret.suffixFormat) == 0 || ret.suffixFormat[len(ret.suffixFormat)- 1].Property != "__key__" {
312 » » ret.suffixFormat = append(ret.suffixFormat, ds.IndexColumn{Prope rty: "__key__"})
313 » }
314
315 » // Now we check the start and end cursors.
316 » //
317 » // Cursors are composed of a list of IndexColumns at the beginning, foll owed
318 » // by the raw bytes to use for the suffix. The cursor is only valid if a ll of
319 » // its IndexColumns match our proposed suffixFormat, as calculated above .
320 » if ret.start != nil {
dnj (Google) 2015/08/23 06:50:06 How about: (a) Don't assign "ret.start" or "ret.en
iannucci 2015/08/23 18:19:42 Much better! Thanks...
321 » » if !sortOrdersEqual(q.startCursorColumns, ret.suffixFormat) {
322 » » » return nil, errors.New("gae/memory: start cursor is inva lid for this query.")
323 » » }
324 » » if q.ineqFilter.start != nil && bytes.Compare(ret.start, q.ineqF ilter.start) < 0 {
325 » » » ret.start = q.ineqFilter.start
326 » » }
327 » } else {
328 » » ret.start = q.ineqFilter.start
329 » }
330
331 » if ret.end != nil {
332 » » if !sortOrdersEqual(q.endCursorColumns, ret.suffixFormat) {
333 » » » return nil, errors.New("gae/memory: end cursor is invali d for this query.")
334 » » }
335 » » if q.ineqFilter.end != nil && bytes.Compare(ret.end, q.ineqFilte r.end) > 0 {
336 » » » ret.end = q.ineqFilter.end
337 » » }
338 » } else {
339 » » ret.end = q.ineqFilter.end
340 » }
341
342 » // Finally, verify that we could even /potentially/ do work. If we have
343 » // overlapping range ends, then we don't have anything to do.
344 » if ret.end != nil && bytes.Compare(ret.start, ret.end) >= 0 {
345 » » return nil, errQueryDone
346 » }
347
348 » return ret, nil
192 } 349 }
193 350
194 func (q *queryImpl) numComponents() int { 351 func (q *queryImpl) numComponents() int {
195 numComponents := len(q.order) 352 numComponents := len(q.order)
196 if q.ineqFilter.prop != "" { 353 if q.ineqFilter.prop != "" {
197 » » if q.ineqFilter.low != nil { 354 » » if q.ineqFilter.start != nil {
198 numComponents++ 355 numComponents++
199 } 356 }
200 » » if q.ineqFilter.high != nil { 357 » » if q.ineqFilter.end != nil {
201 numComponents++ 358 numComponents++
202 } 359 }
203 } 360 }
204 for _, v := range q.eqFilters { 361 for _, v := range q.eqFilters {
205 numComponents += len(v) 362 numComponents += len(v)
206 } 363 }
207 if q.ancestor != nil {
208 numComponents++
209 }
210 return numComponents 364 return numComponents
211 } 365 }
212 366
213 func (q *queryImpl) calculateIndex() *ds.IndexDefinition {
214 // as a nod to simplicity in this code, we'll require that a single inde x
215 // is able to service the entire query. E.g. no zigzag merge joins or
216 // multiqueries. This will mean that the user will need to rely on
217 // dev_appserver to tell them what indicies they need for real, and for thier
218 // tests they'll need to specify the missing composite indices manually.
219 //
220 // This COULD lead to an exploding indicies problem, but we can fix that when
221 // we get to it.
222
223 //sortOrders := []qSortBy{}
224
225 return nil
226 }
227
228 // checkMutateClone sees if the query has an error. If not, it clones the query, 367 // checkMutateClone sees if the query has an error. If not, it clones the query,
229 // and assigns the output of `check` to the query error slot. If check returns 368 // and assigns the output of `check` to the query error slot. If check returns
230 // nil, it calls `mutate` on the cloned query. The (possibly new) query is then 369 // nil, it calls `mutate` on the cloned query. The (possibly new) query is then
231 // returned. 370 // returned.
232 func (q *queryImpl) checkMutateClone(check func() error, mutate func(*queryImpl) ) *queryImpl { 371 func (q *queryImpl) checkMutateClone(check func() error, mutate func(*queryImpl) ) *queryImpl {
233 if q.err != nil { 372 if q.err != nil {
234 return q 373 return q
235 } 374 }
236 nq := *q 375 nq := *q
237 nq.eqFilters = make(map[string]map[string]struct{}, len(q.eqFilters)) 376 nq.eqFilters = make(map[string]map[string]struct{}, len(q.eqFilters))
238 for prop, vals := range q.eqFilters { 377 for prop, vals := range q.eqFilters {
239 nq.eqFilters[prop] = make(map[string]struct{}, len(vals)) 378 nq.eqFilters[prop] = make(map[string]struct{}, len(vals))
240 for v := range vals { 379 for v := range vals {
241 nq.eqFilters[prop][v] = struct{}{} 380 nq.eqFilters[prop][v] = struct{}{}
242 } 381 }
243 } 382 }
244 nq.order = make([]ds.IndexColumn, len(q.order)) 383 nq.order = make([]ds.IndexColumn, len(q.order))
245 copy(nq.order, q.order) 384 copy(nq.order, q.order)
246 » nq.project = make(map[string]struct{}, len(q.project)) 385 » nq.project = make([]string, len(q.project))
247 » for f := range q.project { 386 » copy(nq.project, q.project)
248 » » nq.project[f] = struct{}{}
249 » }
250 if check != nil { 387 if check != nil {
251 nq.err = check() 388 nq.err = check()
252 } 389 }
253 if nq.err == nil { 390 if nq.err == nil {
254 mutate(&nq) 391 mutate(&nq)
255 } 392 }
256 return &nq 393 return &nq
257 } 394 }
258 395
259 func (q *queryImpl) Ancestor(k ds.Key) ds.Query { 396 func (q *queryImpl) Ancestor(k ds.Key) ds.Query {
260 return q.checkMutateClone( 397 return q.checkMutateClone(
261 func() error { 398 func() error {
262 if k == nil { 399 if k == nil {
263 // SDK has an explicit nil-check 400 // SDK has an explicit nil-check
264 return errors.New("datastore: nil query ancestor ") 401 return errors.New("datastore: nil query ancestor ")
265 } 402 }
403 if k.Namespace() != q.ns {
404 return fmt.Errorf("bad namespace: %q (expected % q)", k.Namespace(), q.ns)
405 }
266 if !k.Valid(false, globalAppID, q.ns) { 406 if !k.Valid(false, globalAppID, q.ns) {
267 // technically the SDK implementation does a Wei rd Thing (tm) if both the 407 // technically the SDK implementation does a Wei rd Thing (tm) if both the
268 // stringID and intID are set on a key; it only serializes the stringID in 408 // stringID and intID are set on a key; it only serializes the stringID in
269 // the proto. This means that if you set the Anc estor to an invalid key, 409 // the proto. This means that if you set the Anc estor to an invalid key,
270 // you'll never actually hear about it. Instead of doing that insanity, we 410 // you'll never actually hear about it. Instead of doing that insanity, we
271 // just swap to an error here. 411 // just swap to an error here.
272 return ds.ErrInvalidKey 412 return ds.ErrInvalidKey
273 } 413 }
274 » » » if k.Namespace() != q.ns { 414 » » » if q.eqFilters["__ancestor__"] != nil {
275 » » » » return fmt.Errorf("bad namespace: %q (expected % q)", k.Namespace(), q.ns)
276 » » » }
277 » » » if q.ancestor != nil {
278 return errors.New("cannot have more than one anc estor") 415 return errors.New("cannot have more than one anc estor")
279 } 416 }
280 return nil 417 return nil
281 }, 418 },
282 func(q *queryImpl) { 419 func(q *queryImpl) {
283 » » » q.ancestor = k 420 » » » q.addEqFilt("__ancestor__", ds.MkProperty(k))
284 }) 421 })
285 } 422 }
286 423
287 func (q *queryImpl) Distinct() ds.Query { 424 func (q *queryImpl) Distinct() ds.Query {
288 return q.checkMutateClone(nil, func(q *queryImpl) { 425 return q.checkMutateClone(nil, func(q *queryImpl) {
289 q.distinct = true 426 q.distinct = true
290 }) 427 })
291 } 428 }
292 429
430 func (q *queryImpl) addEqFilt(prop string, p ds.Property) []byte {
431 binVal := serialize.ToBytes(p)
432 if _, ok := q.eqFilters[prop]; !ok {
dnj (Google) 2015/08/23 06:50:06 Retain the equality filter map from the initial lo
iannucci 2015/08/23 18:19:42 Done.
433 q.eqFilters[prop] = map[string]struct{}{string(binVal): {}}
434 } else {
435 q.eqFilters[prop][string(binVal)] = struct{}{}
436 }
437 return binVal
438 }
439
293 func (q *queryImpl) Filter(fStr string, val interface{}) ds.Query { 440 func (q *queryImpl) Filter(fStr string, val interface{}) ds.Query {
294 prop := "" 441 prop := ""
295 op := qInvalid 442 op := qInvalid
296 » binVal := "" 443 » p := ds.Property{}
297 return q.checkMutateClone( 444 return q.checkMutateClone(
298 func() error { 445 func() error {
299 var err error 446 var err error
300 prop, op, err = parseFilter(fStr) 447 prop, op, err = parseFilter(fStr)
301 if err != nil { 448 if err != nil {
302 return err 449 return err
303 } 450 }
304 451
305 if q.kind == "" && prop != "__key__" { 452 if q.kind == "" && prop != "__key__" {
306 // https://cloud.google.com/appengine/docs/go/da tastore/queries#Go_Kindless_queries 453 // https://cloud.google.com/appengine/docs/go/da tastore/queries#Go_Kindless_queries
307 return fmt.Errorf( 454 return fmt.Errorf(
308 "kindless queries can only filter on __k ey__, got %q", fStr) 455 "kindless queries can only filter on __k ey__, got %q", fStr)
309 } 456 }
310 457
311 » » » p := ds.Property{} 458 » » » err = p.SetValue(val, ds.ShouldIndex)
dnj (Google) 2015/08/23 06:50:06 if err := p.SetValue(...); err != nil { Why did t
iannucci 2015/08/23 18:19:42 Because we use this value directly as one of the v
312 » » » err = p.SetValue(val, ds.NoIndex)
313 if err != nil { 459 if err != nil {
314 return err 460 return err
315 } 461 }
316 462
317 if p.Type() == ds.PTKey { 463 if p.Type() == ds.PTKey {
318 if !p.Value().(ds.Key).Valid(false, globalAppID, q.ns) { 464 if !p.Value().(ds.Key).Valid(false, globalAppID, q.ns) {
319 return ds.ErrInvalidKey 465 return ds.ErrInvalidKey
320 } 466 }
321 } 467 }
322 468
323 if prop == "__key__" { 469 if prop == "__key__" {
324 if op == qEqual { 470 if op == qEqual {
325 return fmt.Errorf( 471 return fmt.Errorf(
326 "query equality filter on __key_ _ is silly: %q", fStr) 472 "query equality filter on __key_ _ is silly: %q", fStr)
327 } 473 }
328 if p.Type() != ds.PTKey { 474 if p.Type() != ds.PTKey {
329 return fmt.Errorf("__key__ filter value is not a key: %T", val) 475 return fmt.Errorf("__key__ filter value is not a key: %T", val)
330 } 476 }
477 } else if strings.HasPrefix(prop, "__") && strings.HasSu ffix(prop, "__") {
478 return fmt.Errorf("filter on reserved property: %q", prop)
331 } 479 }
332 480
333 if op != qEqual { 481 if op != qEqual {
334 if q.ineqFilter.prop != "" && q.ineqFilter.prop != prop { 482 if q.ineqFilter.prop != "" && q.ineqFilter.prop != prop {
335 return fmt.Errorf( 483 return fmt.Errorf(
336 "inequality filters on multiple properties: %q and %q", 484 "inequality filters on multiple properties: %q and %q",
337 q.ineqFilter.prop, prop) 485 q.ineqFilter.prop, prop)
338 } 486 }
339 if len(q.order) > 0 && q.order[0].Property != pr op { 487 if len(q.order) > 0 && q.order[0].Property != pr op {
340 return fmt.Errorf( 488 return fmt.Errorf(
341 "first sort order must match ine quality filter: %q v %q", 489 "first sort order must match ine quality filter: %q v %q",
342 q.order[0].Property, prop) 490 q.order[0].Property, prop)
343 } 491 }
344 » » » } else if _, ok := q.project[prop]; ok { 492 » » » } else {
345 » » » » return fmt.Errorf( 493 » » » » for _, p := range q.project {
346 » » » » » "cannot project on field which is used i n an equality filter: %q", 494 » » » » » if p == prop {
347 » » » » » prop) 495 » » » » » » return fmt.Errorf(
496 » » » » » » » "cannot project on field which is used in an equality filter: %q",
497 » » » » » » » prop)
498 » » » » » }
499 » » » » }
348 } 500 }
349 binVal = string(serialize.ToBytes(p))
350 return err 501 return err
351 }, 502 },
352 func(q *queryImpl) { 503 func(q *queryImpl) {
353 if op == qEqual { 504 if op == qEqual {
354 // add it to eq filters 505 // add it to eq filters
355 » » » » if _, ok := q.eqFilters[prop]; !ok { 506 » » » » q.addEqFilt(prop, p)
356 » » » » » q.eqFilters[prop] = map[string]struct{}{ binVal: {}}
357 » » » » } else {
358 » » » » » q.eqFilters[prop][binVal] = struct{}{}
359 » » » » }
360 507
361 // remove it from sort orders. 508 // remove it from sort orders.
362 // https://cloud.google.com/appengine/docs/go/da tastore/queries#sort_orders_are_ignored_on_properties_with_equality_filters 509 // https://cloud.google.com/appengine/docs/go/da tastore/queries#sort_orders_are_ignored_on_properties_with_equality_filters
363 toRm := -1 510 toRm := -1
364 for i, o := range q.order { 511 for i, o := range q.order {
365 if o.Property == prop { 512 if o.Property == prop {
366 toRm = i 513 toRm = i
367 break 514 break
368 } 515 }
369 } 516 }
370 if toRm >= 0 { 517 if toRm >= 0 {
371 q.order = append(q.order[:toRm], q.order [toRm+1:]...) 518 q.order = append(q.order[:toRm], q.order [toRm+1:]...)
372 } 519 }
373 } else { 520 } else {
374 q.ineqFilter.prop = prop 521 q.ineqFilter.prop = prop
375 » » » » if q.ineqFilter.constrain(op, binVal) { 522 » » » » if q.ineqFilter.constrain(op, serialize.ToBytes( p)) {
376 q.err = errQueryDone 523 q.err = errQueryDone
377 } 524 }
378 } 525 }
379 }) 526 })
380 } 527 }
381 528
382 func (q *queryImpl) Order(prop string) ds.Query { 529 func (q *queryImpl) Order(prop string) ds.Query {
383 col := ds.IndexColumn{} 530 col := ds.IndexColumn{}
384 return q.checkMutateClone( 531 return q.checkMutateClone(
385 func() error { 532 func() error {
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
425 } 572 }
426 }) 573 })
427 } 574 }
428 575
429 func (q *queryImpl) Project(fieldName ...string) ds.Query { 576 func (q *queryImpl) Project(fieldName ...string) ds.Query {
430 return q.checkMutateClone( 577 return q.checkMutateClone(
431 func() error { 578 func() error {
432 if q.keysOnly { 579 if q.keysOnly {
433 return errors.New("cannot project a keysOnly que ry") 580 return errors.New("cannot project a keysOnly que ry")
434 } 581 }
582 dupCheck := map[string]struct{}{}
435 for _, f := range fieldName { 583 for _, f := range fieldName {
584 if _, ok := dupCheck[f]; ok {
585 return fmt.Errorf("cannot project on the same field twice: %q", f)
586 }
587 dupCheck[f] = struct{}{}
436 if f == "" { 588 if f == "" {
437 return errors.New("cannot project on an empty field name") 589 return errors.New("cannot project on an empty field name")
438 } 590 }
439 » » » » if strings.HasPrefix(f, "__") && strings.HasSuff ix(f, "__") { 591 » » » » if f == "__key__" {
440 » » » » » return fmt.Errorf("cannot project on %q" , f) 592 » » » » » return fmt.Errorf("cannot project on __k ey__")
441 } 593 }
442 if _, ok := q.eqFilters[f]; ok { 594 if _, ok := q.eqFilters[f]; ok {
443 return fmt.Errorf( 595 return fmt.Errorf(
444 "cannot project on field which i s used in an equality filter: %q", f) 596 "cannot project on field which i s used in an equality filter: %q", f)
445 } 597 }
598 for _, p := range q.project {
599 if p == f {
600 return fmt.Errorf("cannot projec t on the same field twice: %q", f)
601 }
602 }
446 } 603 }
447 return nil 604 return nil
448 }, 605 },
449 func(q *queryImpl) { 606 func(q *queryImpl) {
450 » » » for _, f := range fieldName { 607 » » » q.project = append(q.project, fieldName...)
451 » » » » q.project[f] = struct{}{}
452 » » » }
453 }) 608 })
454 } 609 }
455 610
456 func (q *queryImpl) KeysOnly() ds.Query { 611 func (q *queryImpl) KeysOnly() ds.Query {
457 return q.checkMutateClone( 612 return q.checkMutateClone(
458 func() error { 613 func() error {
459 if len(q.project) != 0 { 614 if len(q.project) != 0 {
460 return errors.New("cannot project a keysOnly que ry") 615 return errors.New("cannot project a keysOnly que ry")
461 } 616 }
462 return nil 617 return nil
463 }, 618 },
464 func(q *queryImpl) { 619 func(q *queryImpl) {
465 q.keysOnly = true 620 q.keysOnly = true
466 }) 621 })
467 } 622 }
468 623
469 func (q *queryImpl) Limit(limit int) ds.Query { 624 func (q *queryImpl) Limit(limit int) ds.Query {
470 return q.checkMutateClone( 625 return q.checkMutateClone(
471 func() error { 626 func() error {
627 // nonsensically... ANY negative value means 'unlimited' . *shakes head*
472 if limit < math.MinInt32 || limit > math.MaxInt32 { 628 if limit < math.MinInt32 || limit > math.MaxInt32 {
473 return errors.New("datastore: query limit overfl ow") 629 return errors.New("datastore: query limit overfl ow")
474 } 630 }
475 return nil 631 return nil
476 }, 632 },
477 func(q *queryImpl) { 633 func(q *queryImpl) {
478 q.limit = int32(limit) 634 q.limit = int32(limit)
479 }) 635 })
480 } 636 }
481 637
482 func (q *queryImpl) Offset(offset int) ds.Query { 638 func (q *queryImpl) Offset(offset int) ds.Query {
483 return q.checkMutateClone( 639 return q.checkMutateClone(
484 func() error { 640 func() error {
485 if offset < 0 { 641 if offset < 0 {
486 return errors.New("datastore: negative query off set") 642 return errors.New("datastore: negative query off set")
487 } 643 }
488 if offset > math.MaxInt32 { 644 if offset > math.MaxInt32 {
489 return errors.New("datastore: query offset overf low") 645 return errors.New("datastore: query offset overf low")
490 } 646 }
491 return nil 647 return nil
492 }, 648 },
493 func(q *queryImpl) { 649 func(q *queryImpl) {
494 q.offset = int32(offset) 650 q.offset = int32(offset)
495 }) 651 })
496 } 652 }
497 653
654 func queryCursorCheck(ns, flavor string, current []byte, newCursor ds.Cursor) ([ ]ds.IndexColumn, []byte, error) {
655 if current != nil {
656 return nil, nil, fmt.Errorf("%s cursor is multiply defined", fla vor)
657 }
658 curs, ok := newCursor.(queryCursor)
659 if !ok {
660 return nil, nil, fmt.Errorf("%s cursor is unknown type: %T", fla vor, curs)
661 }
662 return curs.decode()
663 }
664
498 func (q *queryImpl) Start(c ds.Cursor) ds.Query { 665 func (q *queryImpl) Start(c ds.Cursor) ds.Query {
499 » curs := queryCursor("") 666 » cols := []ds.IndexColumn(nil)
667 » curs := []byte(nil)
500 return q.checkMutateClone( 668 return q.checkMutateClone(
501 » » func() error { 669 » » func() (err error) {
502 » » » ok := false 670 » » » cols, curs, err = queryCursorCheck(q.ns, "start", q.star tCursor, c)
503 » » » if curs, ok = c.(queryCursor); !ok { 671 » » » return
504 » » » » return fmt.Errorf("start cursor is unknown type: %T", c)
505 » » » }
506 » » » if !curs.Valid() {
507 » » » » return errors.New("datastore: invalid cursor")
508 » » » }
509 » » » return nil
510 }, 672 },
511 func(q *queryImpl) { 673 func(q *queryImpl) {
512 » » » q.start = curs 674 » » » q.startCursorColumns = cols
675 » » » q.startCursor = curs
513 }) 676 })
514 } 677 }
515 678
516 func (q *queryImpl) End(c ds.Cursor) ds.Query { 679 func (q *queryImpl) End(c ds.Cursor) ds.Query {
517 » curs := queryCursor("") 680 » cols := []ds.IndexColumn(nil)
681 » curs := queryCursor(nil)
518 return q.checkMutateClone( 682 return q.checkMutateClone(
519 » » func() error { 683 » » func() (err error) {
520 » » » ok := false 684 » » » cols, curs, err = queryCursorCheck(q.ns, "end", q.endCur sor, c)
521 » » » if curs, ok = c.(queryCursor); !ok { 685 » » » return
522 » » » » return fmt.Errorf("end cursor is unknown type: % T", c)
523 » » » }
524 » » » if !curs.Valid() {
525 » » » » return errors.New("datastore: invalid cursor")
526 » » » }
527 » » » return nil
528 }, 686 },
529 func(q *queryImpl) { 687 func(q *queryImpl) {
530 » » » q.end = curs 688 » » » q.endCursorColumns = cols
689 » » » q.endCursor = curs
531 }) 690 })
532 } 691 }
533 692
534 func (q *queryImpl) EventualConsistency() ds.Query { 693 func (q *queryImpl) EventualConsistency() ds.Query {
535 return q.checkMutateClone( 694 return q.checkMutateClone(
536 nil, func(q *queryImpl) { 695 nil, func(q *queryImpl) {
537 q.eventualConsistency = true 696 q.eventualConsistency = true
538 }) 697 })
539 } 698 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698