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.
16 "github.com/golang/protobuf/proto"
17 "golang.org/x/net/context"
19 "google.golang.org/appengine/internal"
20 pb "google.golang.org/appengine/internal/datastore"
23 type KeyRangeCollisionError struct {
28 func (e *KeyRangeCollisionError) Error() string {
29 return fmt.Sprintf("datastore: Collision when attempting to allocate range [%d, %d]",
33 type KeyRangeContentionError struct {
38 func (e *KeyRangeContentionError) Error() string {
39 return fmt.Sprintf("datastore: Contention when attempting to allocate range [%d, %d]",
43 // Key represents the datastore key for a stored entity, and is immutable.
53 // Kind returns the key's kind (also known as entity type).
54 func (k *Key) Kind() string {
58 // StringID returns the key's string ID (also known as an entity name or key
59 // name), which may be "".
60 func (k *Key) StringID() string {
64 // IntID returns the key's integer ID, which may be 0.
65 func (k *Key) IntID() int64 {
69 // Parent returns the key's parent key, which may be nil.
70 func (k *Key) Parent() *Key {
74 // AppID returns the key's application ID.
75 func (k *Key) AppID() string {
79 // Namespace returns the key's namespace.
80 func (k *Key) Namespace() string {
84 // Incomplete returns whether the key does not refer to a stored entity.
85 // In particular, whether the key has a zero StringID and a zero IntID.
86 func (k *Key) Incomplete() bool {
87 return k.stringID == "" && k.intID == 0
90 // valid returns whether the key is valid.
91 func (k *Key) valid() bool {
95 for ; k != nil; k = k.parent {
96 if k.kind == "" || k.appID == "" {
99 if k.stringID != "" && k.intID != 0 {
103 if k.parent.Incomplete() {
106 if k.parent.appID != k.appID || k.parent.namespace != k.namespace {
114 // Equal returns whether two keys are equal.
115 func (k *Key) Equal(o *Key) bool {
116 for k != nil && o != nil {
117 if k.kind != o.kind || k.stringID != o.stringID || k.intID != o.intID || k.appID != o.appID || k.namespace != o.namespace {
120 k, o = k.parent, o.parent
125 // root returns the furthest ancestor of a key, which may be itself.
126 func (k *Key) root() *Key {
127 for k.parent != nil {
133 // marshal marshals the key's string representation to the buffer.
134 func (k *Key) marshal(b *bytes.Buffer) {
139 b.WriteString(k.kind)
141 if k.stringID != "" {
142 b.WriteString(k.stringID)
144 b.WriteString(strconv.FormatInt(k.intID, 10))
148 // String returns a string representation of the key.
149 func (k *Key) String() string {
153 b := bytes.NewBuffer(make([]byte, 0, 512))
167 func keyToGobKey(k *Key) *gobKey {
173 StringID: k.stringID,
175 Parent: keyToGobKey(k.parent),
177 Namespace: k.namespace,
181 func gobKeyToKey(gk *gobKey) *Key {
187 stringID: gk.StringID,
189 parent: gobKeyToKey(gk.Parent),
191 namespace: gk.Namespace,
195 func (k *Key) GobEncode() ([]byte, error) {
196 buf := new(bytes.Buffer)
197 if err := gob.NewEncoder(buf).Encode(keyToGobKey(k)); err != nil {
200 return buf.Bytes(), nil
203 func (k *Key) GobDecode(buf []byte) error {
205 if err := gob.NewDecoder(bytes.NewBuffer(buf)).Decode(gk); err != nil {
208 *k = *gobKeyToKey(gk)
212 func (k *Key) MarshalJSON() ([]byte, error) {
213 return []byte(`"` + k.Encode() + `"`), nil
216 func (k *Key) UnmarshalJSON(buf []byte) error {
217 if len(buf) < 2 || buf[0] != '"' || buf[len(buf)-1] != '"' {
218 return errors.New("datastore: bad JSON key")
220 k2, err := DecodeKey(string(buf[1 : len(buf)-1]))
228 // Encode returns an opaque representation of the key
229 // suitable for use in HTML and URLs.
230 // This is compatible with the Python and Java runtimes.
231 func (k *Key) Encode() string {
232 ref := keyToProto("", k)
234 b, err := proto.Marshal(ref)
239 // Trailing padding is stripped.
240 return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=")
243 // DecodeKey decodes a key from the opaque representation returned by Encode.
244 func DecodeKey(encoded string) (*Key, error) {
246 if m := len(encoded) % 4; m != 0 {
247 encoded += strings.Repeat("=", 4-m)
250 b, err := base64.URLEncoding.DecodeString(encoded)
255 ref := new(pb.Reference)
256 if err := proto.Unmarshal(b, ref); err != nil {
260 return protoToKey(ref)
263 // NewIncompleteKey creates a new incomplete key.
264 // kind cannot be empty.
265 func NewIncompleteKey(c context.Context, kind string, parent *Key) *Key {
266 return NewKey(c, kind, "", 0, parent)
269 // NewKey creates a new key.
270 // kind cannot be empty.
271 // Either one or both of stringID and intID must be zero. If both are zero,
272 // the key returned is incomplete.
273 // parent must either be a complete key or nil.
274 func NewKey(c context.Context, kind, stringID string, intID int64, parent *Key) *Key {
275 // If there's a parent key, use its namespace.
276 // Otherwise, use any namespace attached to the context.
279 namespace = parent.namespace
281 namespace = internal.NamespaceFromContext(c)
289 appID: internal.FullyQualifiedAppID(c),
290 namespace: namespace,
294 // AllocateIDs returns a range of n integer IDs with the given kind and parent
295 // combination. kind cannot be empty; parent may be nil. The IDs in the range
296 // returned will not be used by the datastore's automatic ID sequence generator
297 // and may be used with NewKey without conflict.
299 // The range is inclusive at the low end and exclusive at the high end. In
300 // other words, valid intIDs x satisfy low <= x && x < high.
302 // If no error is returned, low + n == high.
303 func AllocateIDs(c context.Context, kind string, parent *Key, n int) (low, high int64, err error) {
305 return 0, 0, errors.New("datastore: AllocateIDs given an empty kind")
308 return 0, 0, fmt.Errorf("datastore: AllocateIDs given a negative count: %d", n)
313 req := &pb.AllocateIdsRequest{
314 ModelKey: keyToProto("", NewIncompleteKey(c, kind, parent)),
315 Size: proto.Int64(int64(n)),
317 res := &pb.AllocateIdsResponse{}
318 if err := internal.Call(c, "datastore_v3", "AllocateIds", req, res); err != nil {
321 // The protobuf is inclusive at both ends. Idiomatic Go (e.g. slices, for loops)
322 // is inclusive at the low end and exclusive at the high end, so we add 1.
324 high = res.GetEnd() + 1
325 if low+int64(n) != high {
326 return 0, 0, fmt.Errorf("datastore: internal error: could not allocate %d IDs", n)
328 return low, high, nil
331 // AllocateIDRange allocates a range of IDs with specific endpoints.
332 // The range is inclusive at both the low and high end. Once these IDs have been
333 // allocated, you can manually assign them to newly created entities.
335 // The Datastore's automatic ID allocator never assigns a key that has already
336 // been allocated (either through automatic ID allocation or through an explicit
337 // AllocateIDs call). As a result, entities written to the given key range will
338 // never be overwritten. However, writing entities with manually assigned keys in
339 // this range may overwrite existing entities (or new entities written by a separate
340 // request), depending on the error returned.
342 // Use this only if you have an existing numeric ID range that you want to reserve
343 // (for example, bulk loading entities that already have IDs). If you don't care
344 // about which IDs you receive, use AllocateIDs instead.
346 // AllocateIDRange returns nil if the range is successfully allocated. If one or more
347 // entities with an ID in the given range already exist, it returns a KeyRangeCollisionError.
348 // If the Datastore has already cached IDs in this range (e.g. from a previous call to
349 // AllocateIDRange), it returns a KeyRangeContentionError. Errors of other types indicate
350 // problems with arguments or an error returned directly from the Datastore.
351 func AllocateIDRange(c context.Context, kind string, parent *Key, start, end int64) (err error) {
353 return errors.New("datastore: AllocateIDRange given an empty kind")
356 if start < 1 || end < 1 {
357 return errors.New("datastore: AllocateIDRange start and end must both be greater than 0")
361 return errors.New("datastore: AllocateIDRange start must be before end")
364 req := &pb.AllocateIdsRequest{
365 ModelKey: keyToProto("", NewIncompleteKey(c, kind, parent)),
366 Max: proto.Int64(end),
368 res := &pb.AllocateIdsResponse{}
369 if err := internal.Call(c, "datastore_v3", "AllocateIds", req, res); err != nil {
373 // Check for collisions, i.e. existing entities with IDs in this range.
374 // We could do this before the allocation, but we'd still have to do it
375 // afterward as well to catch the race condition where an entity is inserted
376 // after that initial check but before the allocation. Skip the up-front check
377 // and just do it once.
378 q := NewQuery(kind).Filter("__key__ >=", NewKey(c, kind, "", start, parent)).
379 Filter("__key__ <=", NewKey(c, kind, "", end, parent)).KeysOnly().Limit(1)
381 keys, err := q.GetAll(c, nil)
386 return &KeyRangeCollisionError{start: start, end: end}
389 // Check for a race condition, i.e. cases where the datastore may have
390 // cached ID batches that contain IDs in this range.
391 if start < res.GetStart() {
392 return &KeyRangeContentionError{start: start, end: end}