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 "errors" | 9 "errors" |
10 "fmt" | 10 "fmt" |
11 "math" | 11 "math" |
12 "strings" | 12 "strings" |
13 | 13 |
14 » "infra/gae/libs/gae" | 14 » "appengine/datastore" |
15 » "infra/gae/libs/gae/helper" | 15 » pb "appengine_internal/datastore" |
16 | 16 |
17 "github.com/luci/gkvlite" | 17 "github.com/luci/gkvlite" |
18 "github.com/luci/luci-go/common/cmpbin" | 18 "github.com/luci/luci-go/common/cmpbin" |
| 19 |
| 20 "infra/gae/libs/wrapper" |
19 ) | 21 ) |
20 | 22 |
21 type qDirection bool | 23 type qDirection bool |
22 | 24 |
23 const ( | 25 const ( |
24 qASC qDirection = true | 26 qASC qDirection = true |
25 qDEC = false | 27 qDEC = false |
26 ) | 28 ) |
27 | 29 |
28 var builtinQueryPrefix = []byte{0} | 30 var builtinQueryPrefix = []byte{0} |
29 var complexQueryPrefix = []byte{1} | 31 var complexQueryPrefix = []byte{1} |
30 | 32 |
31 type qSortBy struct { | 33 type qSortBy struct { |
32 prop string | 34 prop string |
33 dir qDirection | 35 dir qDirection |
34 } | 36 } |
35 | 37 |
36 func (q qSortBy) WriteBinary(buf *bytes.Buffer) { | 38 func (q qSortBy) WriteBinary(buf *bytes.Buffer) { |
37 if q.dir == qASC { | 39 if q.dir == qASC { |
38 buf.WriteByte(0) | 40 buf.WriteByte(0) |
39 } else { | 41 } else { |
40 buf.WriteByte(1) | 42 buf.WriteByte(1) |
41 } | 43 } |
42 » cmpbin.WriteString(buf, q.prop) | 44 » writeString(buf, q.prop) |
43 } | 45 } |
44 | 46 |
45 func (q *qSortBy) ReadBinary(buf *bytes.Buffer) error { | 47 func (q *qSortBy) ReadBinary(buf *bytes.Buffer) error { |
46 dir, err := buf.ReadByte() | 48 dir, err := buf.ReadByte() |
47 if err != nil { | 49 if err != nil { |
48 return err | 50 return err |
49 } | 51 } |
50 q.dir = dir == 0 | 52 q.dir = dir == 0 |
51 » q.prop, _, err = cmpbin.ReadString(buf) | 53 » q.prop, err = readString(buf) |
52 return err | 54 return err |
53 } | 55 } |
54 | 56 |
55 type qIndex struct { | 57 type qIndex struct { |
56 kind string | 58 kind string |
57 ancestor bool | 59 ancestor bool |
58 sortby []qSortBy | 60 sortby []qSortBy |
59 } | 61 } |
60 | 62 |
61 func (i *qIndex) Builtin() bool { | 63 func (i *qIndex) Builtin() bool { |
62 return !i.ancestor && len(i.sortby) <= 1 | 64 return !i.ancestor && len(i.sortby) <= 1 |
63 } | 65 } |
64 | 66 |
65 func (i *qIndex) Less(o *qIndex) bool { | |
66 ibuf, obuf := &bytes.Buffer{}, &bytes.Buffer{} | |
67 i.WriteBinary(ibuf) | |
68 o.WriteBinary(obuf) | |
69 return i.String() < o.String() | |
70 } | |
71 | |
72 // Valid verifies that this qIndex doesn't have duplicate sortBy fields. | 67 // Valid verifies that this qIndex doesn't have duplicate sortBy fields. |
73 func (i *qIndex) Valid() bool { | 68 func (i *qIndex) Valid() bool { |
74 names := map[string]bool{} | 69 names := map[string]bool{} |
75 for _, sb := range i.sortby { | 70 for _, sb := range i.sortby { |
76 if names[sb.prop] { | 71 if names[sb.prop] { |
77 return false | 72 return false |
78 } | 73 } |
79 names[sb.prop] = true | 74 names[sb.prop] = true |
80 } | 75 } |
81 return true | 76 return true |
82 } | 77 } |
83 | 78 |
84 func (i *qIndex) WriteBinary(buf *bytes.Buffer) { | 79 func (i *qIndex) WriteBinary(buf *bytes.Buffer) { |
85 // TODO(riannucci): do a Grow call here? | 80 // TODO(riannucci): do a Grow call here? |
86 if i.Builtin() { | 81 if i.Builtin() { |
87 buf.Write(builtinQueryPrefix) | 82 buf.Write(builtinQueryPrefix) |
88 } else { | 83 } else { |
89 buf.Write(complexQueryPrefix) | 84 buf.Write(complexQueryPrefix) |
90 } | 85 } |
91 » cmpbin.WriteString(buf, i.kind) | 86 » writeString(buf, i.kind) |
92 if i.ancestor { | 87 if i.ancestor { |
93 buf.WriteByte(0) | 88 buf.WriteByte(0) |
94 } else { | 89 } else { |
95 buf.WriteByte(1) | 90 buf.WriteByte(1) |
96 } | 91 } |
97 cmpbin.WriteUint(buf, uint64(len(i.sortby))) | 92 cmpbin.WriteUint(buf, uint64(len(i.sortby))) |
98 for _, sb := range i.sortby { | 93 for _, sb := range i.sortby { |
99 sb.WriteBinary(buf) | 94 sb.WriteBinary(buf) |
100 } | 95 } |
101 } | 96 } |
(...skipping 20 matching lines...) Expand all Loading... |
122 return ret.String() | 117 return ret.String() |
123 } | 118 } |
124 | 119 |
125 func (i *qIndex) ReadBinary(buf *bytes.Buffer) error { | 120 func (i *qIndex) ReadBinary(buf *bytes.Buffer) error { |
126 // discard builtin/complex byte | 121 // discard builtin/complex byte |
127 _, err := buf.ReadByte() | 122 _, err := buf.ReadByte() |
128 if err != nil { | 123 if err != nil { |
129 return err | 124 return err |
130 } | 125 } |
131 | 126 |
132 » i.kind, _, err = cmpbin.ReadString(buf) | 127 » i.kind, err = readString(buf) |
133 if err != nil { | 128 if err != nil { |
134 return err | 129 return err |
135 } | 130 } |
136 anc, err := buf.ReadByte() | 131 anc, err := buf.ReadByte() |
137 if err != nil { | 132 if err != nil { |
138 return err | 133 return err |
139 } | 134 } |
140 i.ancestor = anc == 1 | 135 i.ancestor = anc == 1 |
141 | 136 |
142 numSorts, _, err := cmpbin.ReadUint(buf) | 137 numSorts, _, err := cmpbin.ReadUint(buf) |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 field string | 206 field string |
212 direction qDirection | 207 direction qDirection |
213 } | 208 } |
214 | 209 |
215 type queryCursor string | 210 type queryCursor string |
216 | 211 |
217 func (q queryCursor) String() string { return string(q) } | 212 func (q queryCursor) String() string { return string(q) } |
218 func (q queryCursor) Valid() bool { return q != "" } | 213 func (q queryCursor) Valid() bool { return q != "" } |
219 | 214 |
220 type queryImpl struct { | 215 type queryImpl struct { |
221 » gae.DSQuery | 216 » wrapper.DSQuery |
222 | 217 |
223 ns string | 218 ns string |
224 | 219 |
225 kind string | 220 kind string |
226 » ancestor gae.DSKey | 221 » ancestor *datastore.Key |
227 filter []queryFilter | 222 filter []queryFilter |
228 order []queryOrder | 223 order []queryOrder |
229 | 224 |
230 keysOnly bool | 225 keysOnly bool |
231 limit int32 | 226 limit int32 |
232 offset int32 | 227 offset int32 |
233 | 228 |
234 start queryCursor | 229 start queryCursor |
235 end queryCursor | 230 end queryCursor |
236 | 231 |
237 err error | 232 err error |
238 } | 233 } |
239 | 234 |
240 type queryIterImpl struct { | 235 type queryIterImpl struct { |
241 idx *queryImpl | 236 idx *queryImpl |
242 } | 237 } |
243 | 238 |
244 func (q *queryIterImpl) Cursor() (gae.DSCursor, error) { | 239 func (q *queryIterImpl) Cursor() (wrapper.DSCursor, error) { |
245 if q.idx.err != nil { | 240 if q.idx.err != nil { |
246 return nil, q.idx.err | 241 return nil, q.idx.err |
247 } | 242 } |
248 return nil, nil | 243 return nil, nil |
249 } | 244 } |
250 | 245 |
251 func (q *queryIterImpl) Next(dst interface{}) (gae.DSKey, error) { | 246 func (q *queryIterImpl) Next(dst interface{}) (*datastore.Key, error) { |
252 if q.idx.err != nil { | 247 if q.idx.err != nil { |
253 return nil, q.idx.err | 248 return nil, q.idx.err |
254 } | 249 } |
255 return nil, nil | 250 return nil, nil |
256 } | 251 } |
257 | 252 |
258 func (q *queryImpl) normalize() (ret *queryImpl) { | 253 func (q *queryImpl) normalize() (ret *queryImpl) { |
259 // ported from GAE SDK datastore_index.py;Normalize() | 254 // ported from GAE SDK datastore_index.py;Normalize() |
260 ret = q.clone() | 255 ret = q.clone() |
261 | 256 |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
333 ret.order = newOrders | 328 ret.order = newOrders |
334 | 329 |
335 return | 330 return |
336 } | 331 } |
337 | 332 |
338 func (q *queryImpl) checkCorrectness(ns string, isTxn bool) (ret *queryImpl) { | 333 func (q *queryImpl) checkCorrectness(ns string, isTxn bool) (ret *queryImpl) { |
339 // ported from GAE SDK datastore_stub_util.py;CheckQuery() | 334 // ported from GAE SDK datastore_stub_util.py;CheckQuery() |
340 ret = q.clone() | 335 ret = q.clone() |
341 | 336 |
342 if ns != ret.ns { | 337 if ns != ret.ns { |
343 » » ret.err = errors.New( | 338 » » ret.err = newDSError(pb.Error_BAD_REQUEST, |
344 » » » "gae/memory: Namespace mismatched. Query and Datastore d
on't agree " + | 339 » » » "MADE UP ERROR: Namespace mismatched. Query and Datastor
e don't agree "+ |
345 "on the current namespace") | 340 "on the current namespace") |
346 return | 341 return |
347 } | 342 } |
348 | 343 |
349 if ret.err != nil { | 344 if ret.err != nil { |
350 return | 345 return |
351 } | 346 } |
352 | 347 |
353 // if projection && keys_only: | 348 // if projection && keys_only: |
354 // "projection and keys_only cannot both be set" | 349 // "projection and keys_only cannot both be set" |
355 | 350 |
356 // if projection props match /^__.*__$/: | 351 // if projection props match /^__.*__$/: |
357 // "projections are not supported for the property: %(prop)s" | 352 // "projections are not supported for the property: %(prop)s" |
358 | 353 |
359 if isTxn && ret.ancestor == nil { | 354 if isTxn && ret.ancestor == nil { |
360 » » ret.err = errors.New( | 355 » » ret.err = newDSError(pb.Error_BAD_REQUEST, |
361 » » » "gae/memory: Only ancestor queries are allowed inside tr
ansactions") | 356 » » » "Only ancestor queries are allowed inside transactions") |
362 return | 357 return |
363 } | 358 } |
364 | 359 |
365 numComponents := len(ret.filter) + len(ret.order) | 360 numComponents := len(ret.filter) + len(ret.order) |
366 if ret.ancestor != nil { | 361 if ret.ancestor != nil { |
367 numComponents++ | 362 numComponents++ |
368 } | 363 } |
369 if numComponents > 100 { | 364 if numComponents > 100 { |
370 » » ret.err = errors.New( | 365 » » ret.err = newDSError(pb.Error_BAD_REQUEST, |
371 » » » "gae/memory: query is too large. may not have more than
" + | 366 » » » "query is too large. may not have more than "+ |
372 "100 filters + sort orders ancestor total") | 367 "100 filters + sort orders ancestor total") |
373 } | 368 } |
374 | 369 |
375 // if ret.ancestor.appid() != current appid | 370 // if ret.ancestor.appid() != current appid |
376 // "query app is x but ancestor app is x" | 371 // "query app is x but ancestor app is x" |
377 // if ret.ancestor.namespace() != current namespace | 372 // if ret.ancestor.namespace() != current namespace |
378 // "query namespace is x but ancestor namespace is x" | 373 // "query namespace is x but ancestor namespace is x" |
379 | 374 |
380 // if not all(g in orders for g in group_by) | 375 // if not all(g in orders for g in group_by) |
381 // "items in the group by clause must be specified first in the orderin
g" | 376 // "items in the group by clause must be specified first in the orderin
g" |
382 | 377 |
383 ineqPropName := "" | 378 ineqPropName := "" |
384 for _, f := range ret.filter { | 379 for _, f := range ret.filter { |
385 if f.field == "__key__" { | 380 if f.field == "__key__" { |
386 » » » k, ok := f.value.(gae.DSKey) | 381 » » » k, ok := f.value.(*datastore.Key) |
387 if !ok { | 382 if !ok { |
388 » » » » ret.err = errors.New( | 383 » » » » ret.err = newDSError(pb.Error_BAD_REQUEST, |
389 » » » » » "gae/memory: __key__ filter value must b
e a Key") | 384 » » » » » "__key__ filter value must be a Key") |
390 return | 385 return |
391 } | 386 } |
392 » » » if !helper.DSKeyValid(k, ret.ns, false) { | 387 » » » if !keyValid(ret.ns, k, userKeyOnly) { |
393 // See the comment in queryImpl.Ancestor; basica
lly this check | 388 // See the comment in queryImpl.Ancestor; basica
lly this check |
394 // never happens in the real env because the SDK
silently swallows | 389 // never happens in the real env because the SDK
silently swallows |
395 // this condition :/ | 390 // this condition :/ |
396 » » » » ret.err = gae.ErrDSInvalidKey | 391 » » » » ret.err = datastore.ErrInvalidKey |
397 return | 392 return |
398 } | 393 } |
399 // __key__ filter app is X but query app is X | 394 // __key__ filter app is X but query app is X |
400 // __key__ filter namespace is X but query namespace is
X | 395 // __key__ filter namespace is X but query namespace is
X |
401 } | 396 } |
402 // if f.op == qEqual and f.field in ret.project_fields | 397 // if f.op == qEqual and f.field in ret.project_fields |
403 // "cannot use projection on a proprety with an equality filte
r" | 398 // "cannot use projection on a proprety with an equality filte
r" |
404 | 399 |
405 if f.op.isINEQOp() { | 400 if f.op.isINEQOp() { |
406 if ineqPropName == "" { | 401 if ineqPropName == "" { |
407 ineqPropName = f.field | 402 ineqPropName = f.field |
408 } else if f.field != ineqPropName { | 403 } else if f.field != ineqPropName { |
409 » » » » ret.err = fmt.Errorf( | 404 » » » » ret.err = newDSError(pb.Error_BAD_REQUEST, |
410 » » » » » "gae/memory: Only one inequality filter
per query is supported. "+ | 405 » » » » » fmt.Sprintf( |
411 » » » » » » "Encountered both %s and %s", in
eqPropName, f.field) | 406 » » » » » » "Only one inequality filter per
query is supported. "+ |
| 407 » » » » » » » "Encountered both %s and
%s", ineqPropName, f.field)) |
412 return | 408 return |
413 } | 409 } |
414 } | 410 } |
415 } | 411 } |
416 | 412 |
417 // if ineqPropName != "" && len(group_by) > 0 && len(orders) ==0 | 413 // if ineqPropName != "" && len(group_by) > 0 && len(orders) ==0 |
418 // "Inequality filter on X must also be a group by property "+ | 414 // "Inequality filter on X must also be a group by property "+ |
419 // "when group by properties are set." | 415 // "when group by properties are set." |
420 | 416 |
421 if ineqPropName != "" && len(ret.order) != 0 { | 417 if ineqPropName != "" && len(ret.order) != 0 { |
422 if ret.order[0].field != ineqPropName { | 418 if ret.order[0].field != ineqPropName { |
423 » » » ret.err = fmt.Errorf( | 419 » » » ret.err = newDSError(pb.Error_BAD_REQUEST, |
424 » » » » "gae/memory: The first sort property must be the
same as the property "+ | 420 » » » » fmt.Sprintf( |
425 » » » » » "to which the inequality filter is appli
ed. In your query "+ | 421 » » » » » "The first sort property must be the sam
e as the property "+ |
426 » » » » » "the first sort property is %s but the i
nequality filter "+ | 422 » » » » » » "to which the inequality filter
is applied. In your query "+ |
427 » » » » » "is on %s", ret.order[0].field, ineqProp
Name) | 423 » » » » » » "the first sort property is %s b
ut the inequality filter "+ |
| 424 » » » » » » "is on %s", ret.order[0].field,
ineqPropName)) |
428 return | 425 return |
429 } | 426 } |
430 } | 427 } |
431 | 428 |
432 if ret.kind == "" { | 429 if ret.kind == "" { |
433 for _, f := range ret.filter { | 430 for _, f := range ret.filter { |
434 if f.field != "__key__" { | 431 if f.field != "__key__" { |
435 » » » » ret.err = errors.New( | 432 » » » » ret.err = newDSError(pb.Error_BAD_REQUEST, |
436 » » » » » "gae/memory: kind is required for non-__
key__ filters") | 433 » » » » » "kind is required for non-__key__ filter
s") |
437 return | 434 return |
438 } | 435 } |
439 } | 436 } |
440 for _, o := range ret.order { | 437 for _, o := range ret.order { |
441 if o.field != "__key__" || o.direction != qASC { | 438 if o.field != "__key__" || o.direction != qASC { |
442 » » » » ret.err = errors.New( | 439 » » » » ret.err = newDSError(pb.Error_BAD_REQUEST, |
443 » » » » » "gae/memory: kind is required for all or
ders except __key__ ascending") | 440 » » » » » "kind is required for all orders except
__key__ ascending") |
444 return | 441 return |
445 } | 442 } |
446 } | 443 } |
447 } | 444 } |
448 return | 445 return |
449 } | 446 } |
450 | 447 |
451 func (q *queryImpl) calculateIndex() *qIndex { | 448 func (q *queryImpl) calculateIndex() *qIndex { |
452 // as a nod to simplicity in this code, we'll require that a single inde
x | 449 // as a nod to simplicity in this code, we'll require that a single inde
x |
453 // is able to service the entire query. E.g. no zigzag merge joins or | 450 // is able to service the entire query. E.g. no zigzag merge joins or |
454 // multiqueries. This will mean that the user will need to rely on | 451 // multiqueries. This will mean that the user will need to rely on |
455 // dev_appserver to tell them what indicies they need for real, and for
thier | 452 // dev_appserver to tell them what indicies they need for real, and for
thier |
456 // tests they'll need to specify the missing composite indices manually. | 453 // tests they'll need to specify the missing composite indices manually. |
457 // | 454 // |
458 // This COULD lead to an exploding indicies problem, but we can fix that
when | 455 // This COULD lead to an exploding indicies problem, but we can fix that
when |
459 // we get to it. | 456 // we get to it. |
460 | 457 |
461 //sortOrders := []qSortBy{} | 458 //sortOrders := []qSortBy{} |
462 | 459 |
463 return nil | 460 return nil |
464 } | 461 } |
465 | 462 |
466 func (q *queryImpl) clone() *queryImpl { | 463 func (q *queryImpl) clone() *queryImpl { |
467 ret := *q | 464 ret := *q |
468 ret.filter = append([]queryFilter(nil), q.filter...) | 465 ret.filter = append([]queryFilter(nil), q.filter...) |
469 ret.order = append([]queryOrder(nil), q.order...) | 466 ret.order = append([]queryOrder(nil), q.order...) |
470 return &ret | 467 return &ret |
471 } | 468 } |
472 | 469 |
473 func (q *queryImpl) Ancestor(k gae.DSKey) gae.DSQuery { | 470 func (q *queryImpl) Ancestor(k *datastore.Key) wrapper.DSQuery { |
474 q = q.clone() | 471 q = q.clone() |
475 q.ancestor = k | 472 q.ancestor = k |
476 if k == nil { | 473 if k == nil { |
477 // SDK has an explicit nil-check | 474 // SDK has an explicit nil-check |
478 q.err = errors.New("datastore: nil query ancestor") | 475 q.err = errors.New("datastore: nil query ancestor") |
479 » } else if !helper.DSKeyValid(k, q.ns, false) { | 476 » } else if !keyValid(q.ns, k, userKeyOnly) { |
480 // technically the SDK implementation does a Weird Thing (tm) if
both the | 477 // technically the SDK implementation does a Weird Thing (tm) if
both the |
481 // stringID and intID are set on a key; it only serializes the s
tringID in | 478 // stringID and intID are set on a key; it only serializes the s
tringID in |
482 // the proto. This means that if you set the Ancestor to an inva
lid key, | 479 // the proto. This means that if you set the Ancestor to an inva
lid key, |
483 // you'll never actually hear about it. Instead of doing that in
sanity, we | 480 // you'll never actually hear about it. Instead of doing that in
sanity, we |
484 // just swap to an error here. | 481 // just swap to an error here. |
485 » » q.err = gae.ErrDSInvalidKey | 482 » » q.err = datastore.ErrInvalidKey |
486 } | 483 } |
487 return q | 484 return q |
488 } | 485 } |
489 | 486 |
490 func (q *queryImpl) Filter(fStr string, val interface{}) gae.DSQuery { | 487 func (q *queryImpl) Filter(fStr string, val interface{}) wrapper.DSQuery { |
491 q = q.clone() | 488 q = q.clone() |
492 f, err := parseFilter(fStr, val) | 489 f, err := parseFilter(fStr, val) |
493 if err != nil { | 490 if err != nil { |
494 q.err = err | 491 q.err = err |
495 return q | 492 return q |
496 } | 493 } |
497 q.filter = append(q.filter, f) | 494 q.filter = append(q.filter, f) |
498 return q | 495 return q |
499 } | 496 } |
500 | 497 |
501 func (q *queryImpl) Order(field string) gae.DSQuery { | 498 func (q *queryImpl) Order(field string) wrapper.DSQuery { |
502 q = q.clone() | 499 q = q.clone() |
503 field = strings.TrimSpace(field) | 500 field = strings.TrimSpace(field) |
504 o := queryOrder{field, qASC} | 501 o := queryOrder{field, qASC} |
505 if strings.HasPrefix(field, "-") { | 502 if strings.HasPrefix(field, "-") { |
506 o.direction = qDEC | 503 o.direction = qDEC |
507 o.field = strings.TrimSpace(field[1:]) | 504 o.field = strings.TrimSpace(field[1:]) |
508 } else if strings.HasPrefix(field, "+") { | 505 } else if strings.HasPrefix(field, "+") { |
509 q.err = fmt.Errorf("datastore: invalid order: %q", field) | 506 q.err = fmt.Errorf("datastore: invalid order: %q", field) |
510 return q | 507 return q |
511 } | 508 } |
512 if len(o.field) == 0 { | 509 if len(o.field) == 0 { |
513 q.err = errors.New("datastore: empty order") | 510 q.err = errors.New("datastore: empty order") |
514 return q | 511 return q |
515 } | 512 } |
516 q.order = append(q.order, o) | 513 q.order = append(q.order, o) |
517 return q | 514 return q |
518 } | 515 } |
519 | 516 |
520 func (q *queryImpl) KeysOnly() gae.DSQuery { | 517 func (q *queryImpl) KeysOnly() wrapper.DSQuery { |
521 q = q.clone() | 518 q = q.clone() |
522 q.keysOnly = true | 519 q.keysOnly = true |
523 return q | 520 return q |
524 } | 521 } |
525 | 522 |
526 func (q *queryImpl) Limit(limit int) gae.DSQuery { | 523 func (q *queryImpl) Limit(limit int) wrapper.DSQuery { |
527 q = q.clone() | 524 q = q.clone() |
528 if limit < math.MinInt32 || limit > math.MaxInt32 { | 525 if limit < math.MinInt32 || limit > math.MaxInt32 { |
529 q.err = errors.New("datastore: query limit overflow") | 526 q.err = errors.New("datastore: query limit overflow") |
530 return q | 527 return q |
531 } | 528 } |
532 q.limit = int32(limit) | 529 q.limit = int32(limit) |
533 return q | 530 return q |
534 } | 531 } |
535 | 532 |
536 func (q *queryImpl) Offset(offset int) gae.DSQuery { | 533 func (q *queryImpl) Offset(offset int) wrapper.DSQuery { |
537 q = q.clone() | 534 q = q.clone() |
538 if offset < 0 { | 535 if offset < 0 { |
539 q.err = errors.New("datastore: negative query offset") | 536 q.err = errors.New("datastore: negative query offset") |
540 return q | 537 return q |
541 } | 538 } |
542 if offset > math.MaxInt32 { | 539 if offset > math.MaxInt32 { |
543 q.err = errors.New("datastore: query offset overflow") | 540 q.err = errors.New("datastore: query offset overflow") |
544 return q | 541 return q |
545 } | 542 } |
546 q.offset = int32(offset) | 543 q.offset = int32(offset) |
547 return q | 544 return q |
548 } | 545 } |
549 | 546 |
550 func (q *queryImpl) Start(c gae.DSCursor) gae.DSQuery { | 547 func (q *queryImpl) Start(c wrapper.DSCursor) wrapper.DSQuery { |
551 q = q.clone() | 548 q = q.clone() |
552 curs := c.(queryCursor) | 549 curs := c.(queryCursor) |
553 if !curs.Valid() { | 550 if !curs.Valid() { |
554 q.err = errors.New("datastore: invalid cursor") | 551 q.err = errors.New("datastore: invalid cursor") |
555 return q | 552 return q |
556 } | 553 } |
557 q.start = curs | 554 q.start = curs |
558 return q | 555 return q |
559 } | 556 } |
560 | 557 |
561 func (q *queryImpl) End(c gae.DSCursor) gae.DSQuery { | 558 func (q *queryImpl) End(c wrapper.DSCursor) wrapper.DSQuery { |
562 q = q.clone() | 559 q = q.clone() |
563 curs := c.(queryCursor) | 560 curs := c.(queryCursor) |
564 if !curs.Valid() { | 561 if !curs.Valid() { |
565 q.err = errors.New("datastore: invalid cursor") | 562 q.err = errors.New("datastore: invalid cursor") |
566 return q | 563 return q |
567 } | 564 } |
568 q.end = curs | 565 q.end = curs |
569 return q | 566 return q |
570 } | 567 } |
OLD | NEW |