1// Copyright 2014 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package datastore
16
17import (
18	"context"
19	"encoding/base64"
20	"errors"
21	"fmt"
22	"math"
23	"reflect"
24	"strconv"
25	"strings"
26
27	"cloud.google.com/go/internal/trace"
28	wrapperspb "github.com/golang/protobuf/ptypes/wrappers"
29	"google.golang.org/api/iterator"
30	pb "google.golang.org/genproto/googleapis/datastore/v1"
31)
32
33type operator int
34
35const (
36	lessThan operator = iota + 1
37	lessEq
38	equal
39	greaterEq
40	greaterThan
41
42	keyFieldName = "__key__"
43)
44
45var operatorToProto = map[operator]pb.PropertyFilter_Operator{
46	lessThan:    pb.PropertyFilter_LESS_THAN,
47	lessEq:      pb.PropertyFilter_LESS_THAN_OR_EQUAL,
48	equal:       pb.PropertyFilter_EQUAL,
49	greaterEq:   pb.PropertyFilter_GREATER_THAN_OR_EQUAL,
50	greaterThan: pb.PropertyFilter_GREATER_THAN,
51}
52
53// filter is a conditional filter on query results.
54type filter struct {
55	FieldName string
56	Op        operator
57	Value     interface{}
58}
59
60type sortDirection bool
61
62const (
63	ascending  sortDirection = false
64	descending sortDirection = true
65)
66
67var sortDirectionToProto = map[sortDirection]pb.PropertyOrder_Direction{
68	ascending:  pb.PropertyOrder_ASCENDING,
69	descending: pb.PropertyOrder_DESCENDING,
70}
71
72// order is a sort order on query results.
73type order struct {
74	FieldName string
75	Direction sortDirection
76}
77
78// NewQuery creates a new Query for a specific entity kind.
79//
80// An empty kind means to return all entities, including entities created and
81// managed by other App Engine features, and is called a kindless query.
82// Kindless queries cannot include filters or sort orders on property values.
83func NewQuery(kind string) *Query {
84	return &Query{
85		kind:  kind,
86		limit: -1,
87	}
88}
89
90// Query represents a datastore query.
91type Query struct {
92	kind       string
93	ancestor   *Key
94	filter     []filter
95	order      []order
96	projection []string
97
98	distinct   bool
99	distinctOn []string
100	keysOnly   bool
101	eventual   bool
102	limit      int32
103	offset     int32
104	start      []byte
105	end        []byte
106
107	namespace string
108
109	trans *Transaction
110
111	err error
112}
113
114func (q *Query) clone() *Query {
115	x := *q
116	// Copy the contents of the slice-typed fields to a new backing store.
117	if len(q.filter) > 0 {
118		x.filter = make([]filter, len(q.filter))
119		copy(x.filter, q.filter)
120	}
121	if len(q.order) > 0 {
122		x.order = make([]order, len(q.order))
123		copy(x.order, q.order)
124	}
125	return &x
126}
127
128// Ancestor returns a derivative query with an ancestor filter.
129// The ancestor should not be nil.
130func (q *Query) Ancestor(ancestor *Key) *Query {
131	q = q.clone()
132	if ancestor == nil {
133		q.err = errors.New("datastore: nil query ancestor")
134		return q
135	}
136	q.ancestor = ancestor
137	return q
138}
139
140// EventualConsistency returns a derivative query that returns eventually
141// consistent results.
142// It only has an effect on ancestor queries.
143func (q *Query) EventualConsistency() *Query {
144	q = q.clone()
145	q.eventual = true
146	return q
147}
148
149// Namespace returns a derivative query that is associated with the given
150// namespace.
151//
152// A namespace may be used to partition data for multi-tenant applications.
153// For details, see https://cloud.google.com/datastore/docs/concepts/multitenancy.
154func (q *Query) Namespace(ns string) *Query {
155	q = q.clone()
156	q.namespace = ns
157	return q
158}
159
160// Transaction returns a derivative query that is associated with the given
161// transaction.
162//
163// All reads performed as part of the transaction will come from a single
164// consistent snapshot. Furthermore, if the transaction is set to a
165// serializable isolation level, another transaction cannot concurrently modify
166// the data that is read or modified by this transaction.
167func (q *Query) Transaction(t *Transaction) *Query {
168	q = q.clone()
169	q.trans = t
170	return q
171}
172
173// Filter returns a derivative query with a field-based filter.
174// The filterStr argument must be a field name followed by optional space,
175// followed by an operator, one of ">", "<", ">=", "<=", or "=".
176// Fields are compared against the provided value using the operator.
177// Multiple filters are AND'ed together.
178// Field names which contain spaces, quote marks, or operator characters
179// should be passed as quoted Go string literals as returned by strconv.Quote
180// or the fmt package's %q verb.
181func (q *Query) Filter(filterStr string, value interface{}) *Query {
182	q = q.clone()
183	filterStr = strings.TrimSpace(filterStr)
184	if filterStr == "" {
185		q.err = fmt.Errorf("datastore: invalid filter %q", filterStr)
186		return q
187	}
188	f := filter{
189		FieldName: strings.TrimRight(filterStr, " ><=!"),
190		Value:     value,
191	}
192	switch op := strings.TrimSpace(filterStr[len(f.FieldName):]); op {
193	case "<=":
194		f.Op = lessEq
195	case ">=":
196		f.Op = greaterEq
197	case "<":
198		f.Op = lessThan
199	case ">":
200		f.Op = greaterThan
201	case "=":
202		f.Op = equal
203	default:
204		q.err = fmt.Errorf("datastore: invalid operator %q in filter %q", op, filterStr)
205		return q
206	}
207	var err error
208	f.FieldName, err = unquote(f.FieldName)
209	if err != nil {
210		q.err = fmt.Errorf("datastore: invalid syntax for quoted field name %q", f.FieldName)
211		return q
212	}
213	q.filter = append(q.filter, f)
214	return q
215}
216
217// Order returns a derivative query with a field-based sort order. Orders are
218// applied in the order they are added. The default order is ascending; to sort
219// in descending order prefix the fieldName with a minus sign (-).
220// Field names which contain spaces, quote marks, or the minus sign
221// should be passed as quoted Go string literals as returned by strconv.Quote
222// or the fmt package's %q verb.
223func (q *Query) Order(fieldName string) *Query {
224	q = q.clone()
225	fieldName, dir := strings.TrimSpace(fieldName), ascending
226	if strings.HasPrefix(fieldName, "-") {
227		fieldName, dir = strings.TrimSpace(fieldName[1:]), descending
228	} else if strings.HasPrefix(fieldName, "+") {
229		q.err = fmt.Errorf("datastore: invalid order: %q", fieldName)
230		return q
231	}
232	fieldName, err := unquote(fieldName)
233	if err != nil {
234		q.err = fmt.Errorf("datastore: invalid syntax for quoted field name %q", fieldName)
235		return q
236	}
237	if fieldName == "" {
238		q.err = errors.New("datastore: empty order")
239		return q
240	}
241	q.order = append(q.order, order{
242		Direction: dir,
243		FieldName: fieldName,
244	})
245	return q
246}
247
248// unquote optionally interprets s as a double-quoted or backquoted Go
249// string literal if it begins with the relevant character.
250func unquote(s string) (string, error) {
251	if s == "" || (s[0] != '`' && s[0] != '"') {
252		return s, nil
253	}
254	return strconv.Unquote(s)
255}
256
257// Project returns a derivative query that yields only the given fields. It
258// cannot be used with KeysOnly.
259func (q *Query) Project(fieldNames ...string) *Query {
260	q = q.clone()
261	q.projection = append([]string(nil), fieldNames...)
262	return q
263}
264
265// Distinct returns a derivative query that yields de-duplicated entities with
266// respect to the set of projected fields. It is only used for projection
267// queries. Distinct cannot be used with DistinctOn.
268func (q *Query) Distinct() *Query {
269	q = q.clone()
270	q.distinct = true
271	return q
272}
273
274// DistinctOn returns a derivative query that yields de-duplicated entities with
275// respect to the set of the specified fields. It is only used for projection
276// queries. The field list should be a subset of the projected field list.
277// DistinctOn cannot be used with Distinct.
278func (q *Query) DistinctOn(fieldNames ...string) *Query {
279	q = q.clone()
280	q.distinctOn = fieldNames
281	return q
282}
283
284// KeysOnly returns a derivative query that yields only keys, not keys and
285// entities. It cannot be used with projection queries.
286func (q *Query) KeysOnly() *Query {
287	q = q.clone()
288	q.keysOnly = true
289	return q
290}
291
292// Limit returns a derivative query that has a limit on the number of results
293// returned. A negative value means unlimited.
294func (q *Query) Limit(limit int) *Query {
295	q = q.clone()
296	if limit < math.MinInt32 || limit > math.MaxInt32 {
297		q.err = errors.New("datastore: query limit overflow")
298		return q
299	}
300	q.limit = int32(limit)
301	return q
302}
303
304// Offset returns a derivative query that has an offset of how many keys to
305// skip over before returning results. A negative value is invalid.
306func (q *Query) Offset(offset int) *Query {
307	q = q.clone()
308	if offset < 0 {
309		q.err = errors.New("datastore: negative query offset")
310		return q
311	}
312	if offset > math.MaxInt32 {
313		q.err = errors.New("datastore: query offset overflow")
314		return q
315	}
316	q.offset = int32(offset)
317	return q
318}
319
320// Start returns a derivative query with the given start point.
321func (q *Query) Start(c Cursor) *Query {
322	q = q.clone()
323	q.start = c.cc
324	return q
325}
326
327// End returns a derivative query with the given end point.
328func (q *Query) End(c Cursor) *Query {
329	q = q.clone()
330	q.end = c.cc
331	return q
332}
333
334// toProto converts the query to a protocol buffer.
335func (q *Query) toProto(req *pb.RunQueryRequest) error {
336	if len(q.projection) != 0 && q.keysOnly {
337		return errors.New("datastore: query cannot both project and be keys-only")
338	}
339	if len(q.distinctOn) != 0 && q.distinct {
340		return errors.New("datastore: query cannot be both distinct and distinct-on")
341	}
342	dst := &pb.Query{}
343	if q.kind != "" {
344		dst.Kind = []*pb.KindExpression{{Name: q.kind}}
345	}
346	if q.projection != nil {
347		for _, propertyName := range q.projection {
348			dst.Projection = append(dst.Projection, &pb.Projection{Property: &pb.PropertyReference{Name: propertyName}})
349		}
350
351		for _, propertyName := range q.distinctOn {
352			dst.DistinctOn = append(dst.DistinctOn, &pb.PropertyReference{Name: propertyName})
353		}
354
355		if q.distinct {
356			for _, propertyName := range q.projection {
357				dst.DistinctOn = append(dst.DistinctOn, &pb.PropertyReference{Name: propertyName})
358			}
359		}
360	}
361	if q.keysOnly {
362		dst.Projection = []*pb.Projection{{Property: &pb.PropertyReference{Name: keyFieldName}}}
363	}
364
365	var filters []*pb.Filter
366	for _, qf := range q.filter {
367		if qf.FieldName == "" {
368			return errors.New("datastore: empty query filter field name")
369		}
370		v, err := interfaceToProto(reflect.ValueOf(qf.Value).Interface(), false)
371		if err != nil {
372			return fmt.Errorf("datastore: bad query filter value type: %v", err)
373		}
374		op, ok := operatorToProto[qf.Op]
375		if !ok {
376			return errors.New("datastore: unknown query filter operator")
377		}
378		xf := &pb.PropertyFilter{
379			Op:       op,
380			Property: &pb.PropertyReference{Name: qf.FieldName},
381			Value:    v,
382		}
383		filters = append(filters, &pb.Filter{
384			FilterType: &pb.Filter_PropertyFilter{PropertyFilter: xf},
385		})
386	}
387
388	if q.ancestor != nil {
389		filters = append(filters, &pb.Filter{
390			FilterType: &pb.Filter_PropertyFilter{PropertyFilter: &pb.PropertyFilter{
391				Property: &pb.PropertyReference{Name: keyFieldName},
392				Op:       pb.PropertyFilter_HAS_ANCESTOR,
393				Value:    &pb.Value{ValueType: &pb.Value_KeyValue{KeyValue: keyToProto(q.ancestor)}},
394			}}})
395	}
396
397	if len(filters) == 1 {
398		dst.Filter = filters[0]
399	} else if len(filters) > 1 {
400		dst.Filter = &pb.Filter{FilterType: &pb.Filter_CompositeFilter{CompositeFilter: &pb.CompositeFilter{
401			Op:      pb.CompositeFilter_AND,
402			Filters: filters,
403		}}}
404	}
405
406	for _, qo := range q.order {
407		if qo.FieldName == "" {
408			return errors.New("datastore: empty query order field name")
409		}
410		xo := &pb.PropertyOrder{
411			Property:  &pb.PropertyReference{Name: qo.FieldName},
412			Direction: sortDirectionToProto[qo.Direction],
413		}
414		dst.Order = append(dst.Order, xo)
415	}
416	if q.limit >= 0 {
417		dst.Limit = &wrapperspb.Int32Value{Value: q.limit}
418	}
419	dst.Offset = q.offset
420	dst.StartCursor = q.start
421	dst.EndCursor = q.end
422
423	if t := q.trans; t != nil {
424		if t.id == nil {
425			return errExpiredTransaction
426		}
427		if q.eventual {
428			return errors.New("datastore: cannot use EventualConsistency query in a transaction")
429		}
430		req.ReadOptions = &pb.ReadOptions{
431			ConsistencyType: &pb.ReadOptions_Transaction{Transaction: t.id},
432		}
433	}
434
435	if q.eventual {
436		req.ReadOptions = &pb.ReadOptions{ConsistencyType: &pb.ReadOptions_ReadConsistency_{ReadConsistency: pb.ReadOptions_EVENTUAL}}
437	}
438
439	req.QueryType = &pb.RunQueryRequest_Query{Query: dst}
440	return nil
441}
442
443// Count returns the number of results for the given query.
444//
445// The running time and number of API calls made by Count scale linearly with
446// the sum of the query's offset and limit. Unless the result count is
447// expected to be small, it is best to specify a limit; otherwise Count will
448// continue until it finishes counting or the provided context expires.
449func (c *Client) Count(ctx context.Context, q *Query) (n int, err error) {
450	ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.Query.Count")
451	defer func() { trace.EndSpan(ctx, err) }()
452
453	// Check that the query is well-formed.
454	if q.err != nil {
455		return 0, q.err
456	}
457
458	// Create a copy of the query, with keysOnly true (if we're not a projection,
459	// since the two are incompatible).
460	newQ := q.clone()
461	newQ.keysOnly = len(newQ.projection) == 0
462
463	// Create an iterator and use it to walk through the batches of results
464	// directly.
465	it := c.Run(ctx, newQ)
466	for {
467		err := it.nextBatch()
468		if err == iterator.Done {
469			return n, nil
470		}
471		if err != nil {
472			return 0, err
473		}
474		n += len(it.results)
475	}
476}
477
478// GetAll runs the provided query in the given context and returns all keys
479// that match that query, as well as appending the values to dst.
480//
481// dst must have type *[]S or *[]*S or *[]P, for some struct type S or some non-
482// interface, non-pointer type P such that P or *P implements PropertyLoadSaver.
483//
484// As a special case, *PropertyList is an invalid type for dst, even though a
485// PropertyList is a slice of structs. It is treated as invalid to avoid being
486// mistakenly passed when *[]PropertyList was intended.
487//
488// The keys returned by GetAll will be in a 1-1 correspondence with the entities
489// added to dst.
490//
491// If q is a ``keys-only'' query, GetAll ignores dst and only returns the keys.
492//
493// The running time and number of API calls made by GetAll scale linearly with
494// with the sum of the query's offset and limit. Unless the result count is
495// expected to be small, it is best to specify a limit; otherwise GetAll will
496// continue until it finishes collecting results or the provided context
497// expires.
498func (c *Client) GetAll(ctx context.Context, q *Query, dst interface{}) (keys []*Key, err error) {
499	ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.Query.GetAll")
500	defer func() { trace.EndSpan(ctx, err) }()
501
502	var (
503		dv               reflect.Value
504		mat              multiArgType
505		elemType         reflect.Type
506		errFieldMismatch error
507	)
508	if !q.keysOnly {
509		dv = reflect.ValueOf(dst)
510		if dv.Kind() != reflect.Ptr || dv.IsNil() {
511			return nil, ErrInvalidEntityType
512		}
513		dv = dv.Elem()
514		mat, elemType = checkMultiArg(dv)
515		if mat == multiArgTypeInvalid || mat == multiArgTypeInterface {
516			return nil, ErrInvalidEntityType
517		}
518	}
519
520	for t := c.Run(ctx, q); ; {
521		k, e, err := t.next()
522		if err == iterator.Done {
523			break
524		}
525		if err != nil {
526			return keys, err
527		}
528		if !q.keysOnly {
529			ev := reflect.New(elemType)
530			if elemType.Kind() == reflect.Map {
531				// This is a special case. The zero values of a map type are
532				// not immediately useful; they have to be make'd.
533				//
534				// Funcs and channels are similar, in that a zero value is not useful,
535				// but even a freshly make'd channel isn't useful: there's no fixed
536				// channel buffer size that is always going to be large enough, and
537				// there's no goroutine to drain the other end. Theoretically, these
538				// types could be supported, for example by sniffing for a constructor
539				// method or requiring prior registration, but for now it's not a
540				// frequent enough concern to be worth it. Programmers can work around
541				// it by explicitly using Iterator.Next instead of the Query.GetAll
542				// convenience method.
543				x := reflect.MakeMap(elemType)
544				ev.Elem().Set(x)
545			}
546			if err = loadEntityProto(ev.Interface(), e); err != nil {
547				if _, ok := err.(*ErrFieldMismatch); ok {
548					// We continue loading entities even in the face of field mismatch errors.
549					// If we encounter any other error, that other error is returned. Otherwise,
550					// an ErrFieldMismatch is returned.
551					errFieldMismatch = err
552				} else {
553					return keys, err
554				}
555			}
556			if mat != multiArgTypeStructPtr {
557				ev = ev.Elem()
558			}
559			dv.Set(reflect.Append(dv, ev))
560		}
561		keys = append(keys, k)
562	}
563	return keys, errFieldMismatch
564}
565
566// Run runs the given query in the given context.
567func (c *Client) Run(ctx context.Context, q *Query) *Iterator {
568	if q.err != nil {
569		return &Iterator{err: q.err}
570	}
571	t := &Iterator{
572		ctx:          ctx,
573		client:       c,
574		limit:        q.limit,
575		offset:       q.offset,
576		keysOnly:     q.keysOnly,
577		pageCursor:   q.start,
578		entityCursor: q.start,
579		req: &pb.RunQueryRequest{
580			ProjectId: c.dataset,
581		},
582	}
583
584	if q.namespace != "" {
585		t.req.PartitionId = &pb.PartitionId{
586			NamespaceId: q.namespace,
587		}
588	}
589
590	if err := q.toProto(t.req); err != nil {
591		t.err = err
592	}
593	return t
594}
595
596// Iterator is the result of running a query.
597//
598// It is not safe for concurrent use.
599type Iterator struct {
600	ctx    context.Context
601	client *Client
602	err    error
603
604	// results is the list of EntityResults still to be iterated over from the
605	// most recent API call. It will be nil if no requests have yet been issued.
606	results []*pb.EntityResult
607	// req is the request to send. It may be modified and used multiple times.
608	req *pb.RunQueryRequest
609
610	// limit is the limit on the number of results this iterator should return.
611	// The zero value is used to prevent further fetches from the server.
612	// A negative value means unlimited.
613	limit int32
614	// offset is the number of results that still need to be skipped.
615	offset int32
616	// keysOnly records whether the query was keys-only (skip entity loading).
617	keysOnly bool
618
619	// pageCursor is the compiled cursor for the next batch/page of result.
620	// TODO(djd): Can we delete this in favour of paging with the last
621	// entityCursor from each batch?
622	pageCursor []byte
623	// entityCursor is the compiled cursor of the next result.
624	entityCursor []byte
625}
626
627// Next returns the key of the next result. When there are no more results,
628// iterator.Done is returned as the error.
629//
630// If the query is not keys only and dst is non-nil, it also loads the entity
631// stored for that key into the struct pointer or PropertyLoadSaver dst, with
632// the same semantics and possible errors as for the Get function.
633func (t *Iterator) Next(dst interface{}) (k *Key, err error) {
634	k, e, err := t.next()
635	if err != nil {
636		return nil, err
637	}
638	if dst != nil && !t.keysOnly {
639		err = loadEntityProto(dst, e)
640	}
641	return k, err
642}
643
644func (t *Iterator) next() (*Key, *pb.Entity, error) {
645	// Fetch additional batches while there are no more results.
646	for t.err == nil && len(t.results) == 0 {
647		t.err = t.nextBatch()
648	}
649	if t.err != nil {
650		return nil, nil, t.err
651	}
652
653	// Extract the next result, update cursors, and parse the entity's key.
654	e := t.results[0]
655	t.results = t.results[1:]
656	t.entityCursor = e.Cursor
657	if len(t.results) == 0 {
658		t.entityCursor = t.pageCursor // At the end of the batch.
659	}
660	if e.Entity.Key == nil {
661		return nil, nil, errors.New("datastore: internal error: server did not return a key")
662	}
663	k, err := protoToKey(e.Entity.Key)
664	if err != nil || k.Incomplete() {
665		return nil, nil, errors.New("datastore: internal error: server returned an invalid key")
666	}
667
668	return k, e.Entity, nil
669}
670
671// nextBatch makes a single call to the server for a batch of results.
672func (t *Iterator) nextBatch() error {
673	if t.err != nil {
674		return t.err
675	}
676
677	if t.limit == 0 {
678		return iterator.Done // Short-circuits the zero-item response.
679	}
680
681	// Adjust the query with the latest start cursor, limit and offset.
682	q := t.req.GetQuery()
683	q.StartCursor = t.pageCursor
684	q.Offset = t.offset
685	if t.limit >= 0 {
686		q.Limit = &wrapperspb.Int32Value{Value: t.limit}
687	} else {
688		q.Limit = nil
689	}
690
691	// Run the query.
692	resp, err := t.client.client.RunQuery(t.ctx, t.req)
693	if err != nil {
694		return err
695	}
696
697	// Adjust any offset from skipped results.
698	skip := resp.Batch.SkippedResults
699	if skip < 0 {
700		return errors.New("datastore: internal error: negative number of skipped_results")
701	}
702	t.offset -= skip
703	if t.offset < 0 {
704		return errors.New("datastore: internal error: query skipped too many results")
705	}
706	if t.offset > 0 && len(resp.Batch.EntityResults) > 0 {
707		return errors.New("datastore: internal error: query returned results before requested offset")
708	}
709
710	// Adjust the limit.
711	if t.limit >= 0 {
712		t.limit -= int32(len(resp.Batch.EntityResults))
713		if t.limit < 0 {
714			return errors.New("datastore: internal error: query returned more results than the limit")
715		}
716	}
717
718	// If there are no more results available, set limit to zero to prevent
719	// further fetches. Otherwise, check that there is a next page cursor available.
720	if resp.Batch.MoreResults != pb.QueryResultBatch_NOT_FINISHED {
721		t.limit = 0
722	} else if resp.Batch.EndCursor == nil {
723		return errors.New("datastore: internal error: server did not return a cursor")
724	}
725
726	// Update cursors.
727	// If any results were skipped, use the SkippedCursor as the next entity cursor.
728	if skip > 0 {
729		t.entityCursor = resp.Batch.SkippedCursor
730	} else {
731		t.entityCursor = q.StartCursor
732	}
733	t.pageCursor = resp.Batch.EndCursor
734
735	t.results = resp.Batch.EntityResults
736	return nil
737}
738
739// Cursor returns a cursor for the iterator's current location.
740func (t *Iterator) Cursor() (c Cursor, err error) {
741	t.ctx = trace.StartSpan(t.ctx, "cloud.google.com/go/datastore.Query.Cursor")
742	defer func() { trace.EndSpan(t.ctx, err) }()
743
744	// If there is still an offset, we need to the skip those results first.
745	for t.err == nil && t.offset > 0 {
746		t.err = t.nextBatch()
747	}
748
749	if t.err != nil && t.err != iterator.Done {
750		return Cursor{}, t.err
751	}
752
753	return Cursor{t.entityCursor}, nil
754}
755
756// Cursor is an iterator's position. It can be converted to and from an opaque
757// string. A cursor can be used from different HTTP requests, but only with a
758// query with the same kind, ancestor, filter and order constraints.
759//
760// The zero Cursor can be used to indicate that there is no start and/or end
761// constraint for a query.
762type Cursor struct {
763	cc []byte
764}
765
766// String returns a base-64 string representation of a cursor.
767func (c Cursor) String() string {
768	if c.cc == nil {
769		return ""
770	}
771
772	return strings.TrimRight(base64.URLEncoding.EncodeToString(c.cc), "=")
773}
774
775// DecodeCursor decodes a cursor from its base-64 string representation.
776func DecodeCursor(s string) (Cursor, error) {
777	if s == "" {
778		return Cursor{}, nil
779	}
780	if n := len(s) % 4; n != 0 {
781		s += strings.Repeat("=", 4-n)
782	}
783	b, err := base64.URLEncoding.DecodeString(s)
784	if err != nil {
785		return Cursor{}, err
786	}
787	return Cursor{b}, nil
788}
789