]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/google.golang.org/appengine/datastore/query.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / google.golang.org / appengine / datastore / query.go
1 // Copyright 2011 Google Inc. All rights reserved.
2 // Use of this source code is governed by the Apache 2.0
3 // license that can be found in the LICENSE file.
4
5 package datastore
6
7 import (
8 "encoding/base64"
9 "errors"
10 "fmt"
11 "math"
12 "reflect"
13 "strings"
14
15 "github.com/golang/protobuf/proto"
16 "golang.org/x/net/context"
17
18 "google.golang.org/appengine/internal"
19 pb "google.golang.org/appengine/internal/datastore"
20 )
21
22 type operator int
23
24 const (
25 lessThan operator = iota
26 lessEq
27 equal
28 greaterEq
29 greaterThan
30 )
31
32 var operatorToProto = map[operator]*pb.Query_Filter_Operator{
33 lessThan: pb.Query_Filter_LESS_THAN.Enum(),
34 lessEq: pb.Query_Filter_LESS_THAN_OR_EQUAL.Enum(),
35 equal: pb.Query_Filter_EQUAL.Enum(),
36 greaterEq: pb.Query_Filter_GREATER_THAN_OR_EQUAL.Enum(),
37 greaterThan: pb.Query_Filter_GREATER_THAN.Enum(),
38 }
39
40 // filter is a conditional filter on query results.
41 type filter struct {
42 FieldName string
43 Op operator
44 Value interface{}
45 }
46
47 type sortDirection int
48
49 const (
50 ascending sortDirection = iota
51 descending
52 )
53
54 var sortDirectionToProto = map[sortDirection]*pb.Query_Order_Direction{
55 ascending: pb.Query_Order_ASCENDING.Enum(),
56 descending: pb.Query_Order_DESCENDING.Enum(),
57 }
58
59 // order is a sort order on query results.
60 type order struct {
61 FieldName string
62 Direction sortDirection
63 }
64
65 // NewQuery creates a new Query for a specific entity kind.
66 //
67 // An empty kind means to return all entities, including entities created and
68 // managed by other App Engine features, and is called a kindless query.
69 // Kindless queries cannot include filters or sort orders on property values.
70 func NewQuery(kind string) *Query {
71 return &Query{
72 kind: kind,
73 limit: -1,
74 }
75 }
76
77 // Query represents a datastore query.
78 type Query struct {
79 kind string
80 ancestor *Key
81 filter []filter
82 order []order
83 projection []string
84
85 distinct bool
86 keysOnly bool
87 eventual bool
88 limit int32
89 offset int32
90 count int32
91 start *pb.CompiledCursor
92 end *pb.CompiledCursor
93
94 err error
95 }
96
97 func (q *Query) clone() *Query {
98 x := *q
99 // Copy the contents of the slice-typed fields to a new backing store.
100 if len(q.filter) > 0 {
101 x.filter = make([]filter, len(q.filter))
102 copy(x.filter, q.filter)
103 }
104 if len(q.order) > 0 {
105 x.order = make([]order, len(q.order))
106 copy(x.order, q.order)
107 }
108 return &x
109 }
110
111 // Ancestor returns a derivative query with an ancestor filter.
112 // The ancestor should not be nil.
113 func (q *Query) Ancestor(ancestor *Key) *Query {
114 q = q.clone()
115 if ancestor == nil {
116 q.err = errors.New("datastore: nil query ancestor")
117 return q
118 }
119 q.ancestor = ancestor
120 return q
121 }
122
123 // EventualConsistency returns a derivative query that returns eventually
124 // consistent results.
125 // It only has an effect on ancestor queries.
126 func (q *Query) EventualConsistency() *Query {
127 q = q.clone()
128 q.eventual = true
129 return q
130 }
131
132 // Filter returns a derivative query with a field-based filter.
133 // The filterStr argument must be a field name followed by optional space,
134 // followed by an operator, one of ">", "<", ">=", "<=", or "=".
135 // Fields are compared against the provided value using the operator.
136 // Multiple filters are AND'ed together.
137 func (q *Query) Filter(filterStr string, value interface{}) *Query {
138 q = q.clone()
139 filterStr = strings.TrimSpace(filterStr)
140 if len(filterStr) < 1 {
141 q.err = errors.New("datastore: invalid filter: " + filterStr)
142 return q
143 }
144 f := filter{
145 FieldName: strings.TrimRight(filterStr, " ><=!"),
146 Value: value,
147 }
148 switch op := strings.TrimSpace(filterStr[len(f.FieldName):]); op {
149 case "<=":
150 f.Op = lessEq
151 case ">=":
152 f.Op = greaterEq
153 case "<":
154 f.Op = lessThan
155 case ">":
156 f.Op = greaterThan
157 case "=":
158 f.Op = equal
159 default:
160 q.err = fmt.Errorf("datastore: invalid operator %q in filter %q", op, filterStr)
161 return q
162 }
163 q.filter = append(q.filter, f)
164 return q
165 }
166
167 // Order returns a derivative query with a field-based sort order. Orders are
168 // applied in the order they are added. The default order is ascending; to sort
169 // in descending order prefix the fieldName with a minus sign (-).
170 func (q *Query) Order(fieldName string) *Query {
171 q = q.clone()
172 fieldName = strings.TrimSpace(fieldName)
173 o := order{
174 Direction: ascending,
175 FieldName: fieldName,
176 }
177 if strings.HasPrefix(fieldName, "-") {
178 o.Direction = descending
179 o.FieldName = strings.TrimSpace(fieldName[1:])
180 } else if strings.HasPrefix(fieldName, "+") {
181 q.err = fmt.Errorf("datastore: invalid order: %q", fieldName)
182 return q
183 }
184 if len(o.FieldName) == 0 {
185 q.err = errors.New("datastore: empty order")
186 return q
187 }
188 q.order = append(q.order, o)
189 return q
190 }
191
192 // Project returns a derivative query that yields only the given fields. It
193 // cannot be used with KeysOnly.
194 func (q *Query) Project(fieldNames ...string) *Query {
195 q = q.clone()
196 q.projection = append([]string(nil), fieldNames...)
197 return q
198 }
199
200 // Distinct returns a derivative query that yields de-duplicated entities with
201 // respect to the set of projected fields. It is only used for projection
202 // queries.
203 func (q *Query) Distinct() *Query {
204 q = q.clone()
205 q.distinct = true
206 return q
207 }
208
209 // KeysOnly returns a derivative query that yields only keys, not keys and
210 // entities. It cannot be used with projection queries.
211 func (q *Query) KeysOnly() *Query {
212 q = q.clone()
213 q.keysOnly = true
214 return q
215 }
216
217 // Limit returns a derivative query that has a limit on the number of results
218 // returned. A negative value means unlimited.
219 func (q *Query) Limit(limit int) *Query {
220 q = q.clone()
221 if limit < math.MinInt32 || limit > math.MaxInt32 {
222 q.err = errors.New("datastore: query limit overflow")
223 return q
224 }
225 q.limit = int32(limit)
226 return q
227 }
228
229 // Offset returns a derivative query that has an offset of how many keys to
230 // skip over before returning results. A negative value is invalid.
231 func (q *Query) Offset(offset int) *Query {
232 q = q.clone()
233 if offset < 0 {
234 q.err = errors.New("datastore: negative query offset")
235 return q
236 }
237 if offset > math.MaxInt32 {
238 q.err = errors.New("datastore: query offset overflow")
239 return q
240 }
241 q.offset = int32(offset)
242 return q
243 }
244
245 // BatchSize returns a derivative query to fetch the supplied number of results
246 // at once. This value should be greater than zero, and equal to or less than
247 // the Limit.
248 func (q *Query) BatchSize(size int) *Query {
249 q = q.clone()
250 if size <= 0 || size > math.MaxInt32 {
251 q.err = errors.New("datastore: query batch size overflow")
252 return q
253 }
254 q.count = int32(size)
255 return q
256 }
257
258 // Start returns a derivative query with the given start point.
259 func (q *Query) Start(c Cursor) *Query {
260 q = q.clone()
261 if c.cc == nil {
262 q.err = errors.New("datastore: invalid cursor")
263 return q
264 }
265 q.start = c.cc
266 return q
267 }
268
269 // End returns a derivative query with the given end point.
270 func (q *Query) End(c Cursor) *Query {
271 q = q.clone()
272 if c.cc == nil {
273 q.err = errors.New("datastore: invalid cursor")
274 return q
275 }
276 q.end = c.cc
277 return q
278 }
279
280 // toProto converts the query to a protocol buffer.
281 func (q *Query) toProto(dst *pb.Query, appID string) error {
282 if len(q.projection) != 0 && q.keysOnly {
283 return errors.New("datastore: query cannot both project and be keys-only")
284 }
285 dst.Reset()
286 dst.App = proto.String(appID)
287 if q.kind != "" {
288 dst.Kind = proto.String(q.kind)
289 }
290 if q.ancestor != nil {
291 dst.Ancestor = keyToProto(appID, q.ancestor)
292 if q.eventual {
293 dst.Strong = proto.Bool(false)
294 }
295 }
296 if q.projection != nil {
297 dst.PropertyName = q.projection
298 if q.distinct {
299 dst.GroupByPropertyName = q.projection
300 }
301 }
302 if q.keysOnly {
303 dst.KeysOnly = proto.Bool(true)
304 dst.RequirePerfectPlan = proto.Bool(true)
305 }
306 for _, qf := range q.filter {
307 if qf.FieldName == "" {
308 return errors.New("datastore: empty query filter field name")
309 }
310 p, errStr := valueToProto(appID, qf.FieldName, reflect.ValueOf(qf.Value), false)
311 if errStr != "" {
312 return errors.New("datastore: bad query filter value type: " + errStr)
313 }
314 xf := &pb.Query_Filter{
315 Op: operatorToProto[qf.Op],
316 Property: []*pb.Property{p},
317 }
318 if xf.Op == nil {
319 return errors.New("datastore: unknown query filter operator")
320 }
321 dst.Filter = append(dst.Filter, xf)
322 }
323 for _, qo := range q.order {
324 if qo.FieldName == "" {
325 return errors.New("datastore: empty query order field name")
326 }
327 xo := &pb.Query_Order{
328 Property: proto.String(qo.FieldName),
329 Direction: sortDirectionToProto[qo.Direction],
330 }
331 if xo.Direction == nil {
332 return errors.New("datastore: unknown query order direction")
333 }
334 dst.Order = append(dst.Order, xo)
335 }
336 if q.limit >= 0 {
337 dst.Limit = proto.Int32(q.limit)
338 }
339 if q.offset != 0 {
340 dst.Offset = proto.Int32(q.offset)
341 }
342 if q.count != 0 {
343 dst.Count = proto.Int32(q.count)
344 }
345 dst.CompiledCursor = q.start
346 dst.EndCompiledCursor = q.end
347 dst.Compile = proto.Bool(true)
348 return nil
349 }
350
351 // Count returns the number of results for the query.
352 //
353 // The running time and number of API calls made by Count scale linearly with
354 // the sum of the query's offset and limit. Unless the result count is
355 // expected to be small, it is best to specify a limit; otherwise Count will
356 // continue until it finishes counting or the provided context expires.
357 func (q *Query) Count(c context.Context) (int, error) {
358 // Check that the query is well-formed.
359 if q.err != nil {
360 return 0, q.err
361 }
362
363 // Run a copy of the query, with keysOnly true (if we're not a projection,
364 // since the two are incompatible), and an adjusted offset. We also set the
365 // limit to zero, as we don't want any actual entity data, just the number
366 // of skipped results.
367 newQ := q.clone()
368 newQ.keysOnly = len(newQ.projection) == 0
369 newQ.limit = 0
370 if q.limit < 0 {
371 // If the original query was unlimited, set the new query's offset to maximum.
372 newQ.offset = math.MaxInt32
373 } else {
374 newQ.offset = q.offset + q.limit
375 if newQ.offset < 0 {
376 // Do the best we can, in the presence of overflow.
377 newQ.offset = math.MaxInt32
378 }
379 }
380 req := &pb.Query{}
381 if err := newQ.toProto(req, internal.FullyQualifiedAppID(c)); err != nil {
382 return 0, err
383 }
384 res := &pb.QueryResult{}
385 if err := internal.Call(c, "datastore_v3", "RunQuery", req, res); err != nil {
386 return 0, err
387 }
388
389 // n is the count we will return. For example, suppose that our original
390 // query had an offset of 4 and a limit of 2008: the count will be 2008,
391 // provided that there are at least 2012 matching entities. However, the
392 // RPCs will only skip 1000 results at a time. The RPC sequence is:
393 // call RunQuery with (offset, limit) = (2012, 0) // 2012 == newQ.offset
394 // response has (skippedResults, moreResults) = (1000, true)
395 // n += 1000 // n == 1000
396 // call Next with (offset, limit) = (1012, 0) // 1012 == newQ.offset - n
397 // response has (skippedResults, moreResults) = (1000, true)
398 // n += 1000 // n == 2000
399 // call Next with (offset, limit) = (12, 0) // 12 == newQ.offset - n
400 // response has (skippedResults, moreResults) = (12, false)
401 // n += 12 // n == 2012
402 // // exit the loop
403 // n -= 4 // n == 2008
404 var n int32
405 for {
406 // The QueryResult should have no actual entity data, just skipped results.
407 if len(res.Result) != 0 {
408 return 0, errors.New("datastore: internal error: Count request returned too much data")
409 }
410 n += res.GetSkippedResults()
411 if !res.GetMoreResults() {
412 break
413 }
414 if err := callNext(c, res, newQ.offset-n, q.count); err != nil {
415 return 0, err
416 }
417 }
418 n -= q.offset
419 if n < 0 {
420 // If the offset was greater than the number of matching entities,
421 // return 0 instead of negative.
422 n = 0
423 }
424 return int(n), nil
425 }
426
427 // callNext issues a datastore_v3/Next RPC to advance a cursor, such as that
428 // returned by a query with more results.
429 func callNext(c context.Context, res *pb.QueryResult, offset, count int32) error {
430 if res.Cursor == nil {
431 return errors.New("datastore: internal error: server did not return a cursor")
432 }
433 req := &pb.NextRequest{
434 Cursor: res.Cursor,
435 }
436 if count >= 0 {
437 req.Count = proto.Int32(count)
438 }
439 if offset != 0 {
440 req.Offset = proto.Int32(offset)
441 }
442 if res.CompiledCursor != nil {
443 req.Compile = proto.Bool(true)
444 }
445 res.Reset()
446 return internal.Call(c, "datastore_v3", "Next", req, res)
447 }
448
449 // GetAll runs the query in the given context and returns all keys that match
450 // that query, as well as appending the values to dst.
451 //
452 // dst must have type *[]S or *[]*S or *[]P, for some struct type S or some non-
453 // interface, non-pointer type P such that P or *P implements PropertyLoadSaver.
454 //
455 // As a special case, *PropertyList is an invalid type for dst, even though a
456 // PropertyList is a slice of structs. It is treated as invalid to avoid being
457 // mistakenly passed when *[]PropertyList was intended.
458 //
459 // The keys returned by GetAll will be in a 1-1 correspondence with the entities
460 // added to dst.
461 //
462 // If q is a ``keys-only'' query, GetAll ignores dst and only returns the keys.
463 //
464 // The running time and number of API calls made by GetAll scale linearly with
465 // the sum of the query's offset and limit. Unless the result count is
466 // expected to be small, it is best to specify a limit; otherwise GetAll will
467 // continue until it finishes collecting results or the provided context
468 // expires.
469 func (q *Query) GetAll(c context.Context, dst interface{}) ([]*Key, error) {
470 var (
471 dv reflect.Value
472 mat multiArgType
473 elemType reflect.Type
474 errFieldMismatch error
475 )
476 if !q.keysOnly {
477 dv = reflect.ValueOf(dst)
478 if dv.Kind() != reflect.Ptr || dv.IsNil() {
479 return nil, ErrInvalidEntityType
480 }
481 dv = dv.Elem()
482 mat, elemType = checkMultiArg(dv)
483 if mat == multiArgTypeInvalid || mat == multiArgTypeInterface {
484 return nil, ErrInvalidEntityType
485 }
486 }
487
488 var keys []*Key
489 for t := q.Run(c); ; {
490 k, e, err := t.next()
491 if err == Done {
492 break
493 }
494 if err != nil {
495 return keys, err
496 }
497 if !q.keysOnly {
498 ev := reflect.New(elemType)
499 if elemType.Kind() == reflect.Map {
500 // This is a special case. The zero values of a map type are
501 // not immediately useful; they have to be make'd.
502 //
503 // Funcs and channels are similar, in that a zero value is not useful,
504 // but even a freshly make'd channel isn't useful: there's no fixed
505 // channel buffer size that is always going to be large enough, and
506 // there's no goroutine to drain the other end. Theoretically, these
507 // types could be supported, for example by sniffing for a constructor
508 // method or requiring prior registration, but for now it's not a
509 // frequent enough concern to be worth it. Programmers can work around
510 // it by explicitly using Iterator.Next instead of the Query.GetAll
511 // convenience method.
512 x := reflect.MakeMap(elemType)
513 ev.Elem().Set(x)
514 }
515 if err = loadEntity(ev.Interface(), e); err != nil {
516 if _, ok := err.(*ErrFieldMismatch); ok {
517 // We continue loading entities even in the face of field mismatch errors.
518 // If we encounter any other error, that other error is returned. Otherwise,
519 // an ErrFieldMismatch is returned.
520 errFieldMismatch = err
521 } else {
522 return keys, err
523 }
524 }
525 if mat != multiArgTypeStructPtr {
526 ev = ev.Elem()
527 }
528 dv.Set(reflect.Append(dv, ev))
529 }
530 keys = append(keys, k)
531 }
532 return keys, errFieldMismatch
533 }
534
535 // Run runs the query in the given context.
536 func (q *Query) Run(c context.Context) *Iterator {
537 if q.err != nil {
538 return &Iterator{err: q.err}
539 }
540 t := &Iterator{
541 c: c,
542 limit: q.limit,
543 count: q.count,
544 q: q,
545 prevCC: q.start,
546 }
547 var req pb.Query
548 if err := q.toProto(&req, internal.FullyQualifiedAppID(c)); err != nil {
549 t.err = err
550 return t
551 }
552 if err := internal.Call(c, "datastore_v3", "RunQuery", &req, &t.res); err != nil {
553 t.err = err
554 return t
555 }
556 offset := q.offset - t.res.GetSkippedResults()
557 var count int32
558 if t.count > 0 && (t.limit < 0 || t.count < t.limit) {
559 count = t.count
560 } else {
561 count = t.limit
562 }
563 for offset > 0 && t.res.GetMoreResults() {
564 t.prevCC = t.res.CompiledCursor
565 if err := callNext(t.c, &t.res, offset, count); err != nil {
566 t.err = err
567 break
568 }
569 skip := t.res.GetSkippedResults()
570 if skip < 0 {
571 t.err = errors.New("datastore: internal error: negative number of skipped_results")
572 break
573 }
574 offset -= skip
575 }
576 if offset < 0 {
577 t.err = errors.New("datastore: internal error: query offset was overshot")
578 }
579 return t
580 }
581
582 // Iterator is the result of running a query.
583 type Iterator struct {
584 c context.Context
585 err error
586 // res is the result of the most recent RunQuery or Next API call.
587 res pb.QueryResult
588 // i is how many elements of res.Result we have iterated over.
589 i int
590 // limit is the limit on the number of results this iterator should return.
591 // A negative value means unlimited.
592 limit int32
593 // count is the number of results this iterator should fetch at once. This
594 // should be equal to or greater than zero.
595 count int32
596 // q is the original query which yielded this iterator.
597 q *Query
598 // prevCC is the compiled cursor that marks the end of the previous batch
599 // of results.
600 prevCC *pb.CompiledCursor
601 }
602
603 // Done is returned when a query iteration has completed.
604 var Done = errors.New("datastore: query has no more results")
605
606 // Next returns the key of the next result. When there are no more results,
607 // Done is returned as the error.
608 //
609 // If the query is not keys only and dst is non-nil, it also loads the entity
610 // stored for that key into the struct pointer or PropertyLoadSaver dst, with
611 // the same semantics and possible errors as for the Get function.
612 func (t *Iterator) Next(dst interface{}) (*Key, error) {
613 k, e, err := t.next()
614 if err != nil {
615 return nil, err
616 }
617 if dst != nil && !t.q.keysOnly {
618 err = loadEntity(dst, e)
619 }
620 return k, err
621 }
622
623 func (t *Iterator) next() (*Key, *pb.EntityProto, error) {
624 if t.err != nil {
625 return nil, nil, t.err
626 }
627
628 // Issue datastore_v3/Next RPCs as necessary.
629 for t.i == len(t.res.Result) {
630 if !t.res.GetMoreResults() {
631 t.err = Done
632 return nil, nil, t.err
633 }
634 t.prevCC = t.res.CompiledCursor
635 var count int32
636 if t.count > 0 && (t.limit < 0 || t.count < t.limit) {
637 count = t.count
638 } else {
639 count = t.limit
640 }
641 if err := callNext(t.c, &t.res, 0, count); err != nil {
642 t.err = err
643 return nil, nil, t.err
644 }
645 if t.res.GetSkippedResults() != 0 {
646 t.err = errors.New("datastore: internal error: iterator has skipped results")
647 return nil, nil, t.err
648 }
649 t.i = 0
650 if t.limit >= 0 {
651 t.limit -= int32(len(t.res.Result))
652 if t.limit < 0 {
653 t.err = errors.New("datastore: internal error: query returned more results than the limit")
654 return nil, nil, t.err
655 }
656 }
657 }
658
659 // Extract the key from the t.i'th element of t.res.Result.
660 e := t.res.Result[t.i]
661 t.i++
662 if e.Key == nil {
663 return nil, nil, errors.New("datastore: internal error: server did not return a key")
664 }
665 k, err := protoToKey(e.Key)
666 if err != nil || k.Incomplete() {
667 return nil, nil, errors.New("datastore: internal error: server returned an invalid key")
668 }
669 return k, e, nil
670 }
671
672 // Cursor returns a cursor for the iterator's current location.
673 func (t *Iterator) Cursor() (Cursor, error) {
674 if t.err != nil && t.err != Done {
675 return Cursor{}, t.err
676 }
677 // If we are at either end of the current batch of results,
678 // return the compiled cursor at that end.
679 skipped := t.res.GetSkippedResults()
680 if t.i == 0 && skipped == 0 {
681 if t.prevCC == nil {
682 // A nil pointer (of type *pb.CompiledCursor) means no constraint:
683 // passing it as the end cursor of a new query means unlimited results
684 // (glossing over the integer limit parameter for now).
685 // A non-nil pointer to an empty pb.CompiledCursor means the start:
686 // passing it as the end cursor of a new query means 0 results.
687 // If prevCC was nil, then the original query had no start cursor, but
688 // Iterator.Cursor should return "the start" instead of unlimited.
689 return Cursor{&zeroCC}, nil
690 }
691 return Cursor{t.prevCC}, nil
692 }
693 if t.i == len(t.res.Result) {
694 return Cursor{t.res.CompiledCursor}, nil
695 }
696 // Otherwise, re-run the query offset to this iterator's position, starting from
697 // the most recent compiled cursor. This is done on a best-effort basis, as it
698 // is racy; if a concurrent process has added or removed entities, then the
699 // cursor returned may be inconsistent.
700 q := t.q.clone()
701 q.start = t.prevCC
702 q.offset = skipped + int32(t.i)
703 q.limit = 0
704 q.keysOnly = len(q.projection) == 0
705 t1 := q.Run(t.c)
706 _, _, err := t1.next()
707 if err != Done {
708 if err == nil {
709 err = fmt.Errorf("datastore: internal error: zero-limit query did not have zero results")
710 }
711 return Cursor{}, err
712 }
713 return Cursor{t1.res.CompiledCursor}, nil
714 }
715
716 var zeroCC pb.CompiledCursor
717
718 // Cursor is an iterator's position. It can be converted to and from an opaque
719 // string. A cursor can be used from different HTTP requests, but only with a
720 // query with the same kind, ancestor, filter and order constraints.
721 type Cursor struct {
722 cc *pb.CompiledCursor
723 }
724
725 // String returns a base-64 string representation of a cursor.
726 func (c Cursor) String() string {
727 if c.cc == nil {
728 return ""
729 }
730 b, err := proto.Marshal(c.cc)
731 if err != nil {
732 // The only way to construct a Cursor with a non-nil cc field is to
733 // unmarshal from the byte representation. We panic if the unmarshal
734 // succeeds but the marshaling of the unchanged protobuf value fails.
735 panic(fmt.Sprintf("datastore: internal error: malformed cursor: %v", err))
736 }
737 return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=")
738 }
739
740 // Decode decodes a cursor from its base-64 string representation.
741 func DecodeCursor(s string) (Cursor, error) {
742 if s == "" {
743 return Cursor{&zeroCC}, nil
744 }
745 if n := len(s) % 4; n != 0 {
746 s += strings.Repeat("=", 4-n)
747 }
748 b, err := base64.URLEncoding.DecodeString(s)
749 if err != nil {
750 return Cursor{}, err
751 }
752 cc := &pb.CompiledCursor{}
753 if err := proto.Unmarshal(b, cc); err != nil {
754 return Cursor{}, err
755 }
756 return Cursor{cc}, nil
757 }