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